From 1eb757ea41fc2d76bedea2da140ed82c3c00fe51 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Thu, 11 Apr 2024 10:26:11 -0400 Subject: [PATCH] Try to not assume testing.T (#79) Generalizing from concrete `*testing.T` allows using pulumitest in other test contexts, such as rapid testing. A new PT interface is added to abstract over concrete testing contexts. Breaking: remove `PulumiTest.T()` accessor: ``` func (a *PulumiTest) T() T ``` --------- Co-authored-by: Daniel Bradley --- previewProviderUpgrade.go | 9 ++++---- previewProviderUpgrade_test.go | 4 ++-- pulumitest/convert.go | 6 ++--- pulumitest/copy.go | 4 ++-- pulumitest/destroy.go | 4 ++-- pulumitest/exportStack.go | 4 ++-- pulumitest/grpcLog.go | 4 ++-- pulumitest/importStack.go | 4 ++-- pulumitest/install.go | 2 +- pulumitest/newStack.go | 26 ++++++++++----------- pulumitest/preview.go | 4 ++-- pulumitest/pulumiTest.go | 41 ++++++++++++++++++++++++++-------- pulumitest/refresh.go | 4 ++-- pulumitest/run.go | 18 +++++++-------- pulumitest/setConfig.go | 4 ++-- pulumitest/testingT.go | 16 +++++++++++++ pulumitest/up.go | 4 ++-- pulumitest/updateSource.go | 2 +- 18 files changed, 100 insertions(+), 60 deletions(-) create mode 100644 pulumitest/testingT.go diff --git a/previewProviderUpgrade.go b/previewProviderUpgrade.go index ff50ead..e53067d 100644 --- a/previewProviderUpgrade.go +++ b/previewProviderUpgrade.go @@ -1,6 +1,7 @@ package providertest import ( + "fmt" "path/filepath" "github.com/pulumi/providertest/optproviderupgrade" @@ -14,8 +15,8 @@ import ( // PreviewProviderUpgrade captures the state of a stack from a baseline provider configuration, then previews the stack // with the current provider configuration. // Uses a default cache directory of "testdata/recorded/TestProviderUpgrade/{programName}/{baselineVersion}". -func PreviewProviderUpgrade(pulumiTest *pulumitest.PulumiTest, providerName string, baselineVersion string, opts ...optproviderupgrade.PreviewProviderUpgradeOpt) auto.PreviewResult { - pulumiTest.T().Helper() +func PreviewProviderUpgrade(t pulumitest.PT, pulumiTest *pulumitest.PulumiTest, providerName string, baselineVersion string, opts ...optproviderupgrade.PreviewProviderUpgradeOpt) auto.PreviewResult { + t.Helper() previewTest := pulumiTest.CopyToTempDir(opttest.NewStackOptions(optnewstack.DisableAutoDestroy())) options := optproviderupgrade.Defaults() for _, opt := range opts { @@ -25,11 +26,11 @@ func PreviewProviderUpgrade(pulumiTest *pulumitest.PulumiTest, providerName stri cacheDir := getCacheDir(options, programName, baselineVersion) previewTest.Run( func(test *pulumitest.PulumiTest) { - test.T().Helper() + t.Helper() test.Up() grptLog := test.GrpcLog() grpcLogPath := filepath.Join(cacheDir, "grpc.json") - test.T().Logf("writing grpc log to %s", grpcLogPath) + t.Log(fmt.Sprintf("writing grpc log to %s", grpcLogPath)) grptLog.WriteTo(grpcLogPath) }, optrun.WithCache(filepath.Join(cacheDir, "stack.json")), diff --git a/previewProviderUpgrade_test.go b/previewProviderUpgrade_test.go index 702225a..4eb92c2 100644 --- a/previewProviderUpgrade_test.go +++ b/previewProviderUpgrade_test.go @@ -18,13 +18,13 @@ func TestPreviewUpgradeCached(t *testing.T) { test := pulumitest.NewPulumiTest(t, filepath.Join("pulumitest", "testdata", "yaml_program"), opttest.DownloadProviderVersion("random", "4.15.0")) - uncachedPreviewResult := providertest.PreviewProviderUpgrade(test, "random", "4.5.0", + uncachedPreviewResult := providertest.PreviewProviderUpgrade(t, test, "random", "4.5.0", optproviderupgrade.CacheDir(cacheDir, "{programName}", "{baselineVersion}"), optproviderupgrade.DisableAttach()) assertpreview.HasNoReplacements(t, uncachedPreviewResult) assertpreview.HasNoChanges(t, uncachedPreviewResult) - cachedPreviewResult := providertest.PreviewProviderUpgrade(test, "random", "4.5.0", + cachedPreviewResult := providertest.PreviewProviderUpgrade(t, test, "random", "4.5.0", optproviderupgrade.CacheDir(cacheDir, "{programName}", "{baselineVersion}"), optproviderupgrade.DisableAttach()) assert.Equal(t, uncachedPreviewResult, cachedPreviewResult, "expected uncached and cached preview to be the same") diff --git a/pulumitest/convert.go b/pulumitest/convert.go index 487a083..e6d2697 100644 --- a/pulumitest/convert.go +++ b/pulumitest/convert.go @@ -26,15 +26,15 @@ func (a *PulumiTest) Convert(language string, opts ...opttest.Option) ConvertRes targetDir := filepath.Join(tempDir, fmt.Sprintf("%s-%s", base, language)) err := os.Mkdir(targetDir, 0755) if err != nil { - a.t.Fatal(err) + a.fatal(err) } - a.t.Logf("converting to %s", language) + a.logf("converting to %s", language) cmd := exec.Command("pulumi", "convert", "--language", language, "--generate-only", "--out", targetDir) cmd.Dir = a.source out, err := cmd.CombinedOutput() if err != nil { - a.t.Fatalf("failed to convert directory: %s\n%s", err, out) + a.fatalf("failed to convert directory: %s\n%s", err, out) } options := a.options.Copy() diff --git a/pulumitest/copy.go b/pulumitest/copy.go index bf877fa..e7d4797 100644 --- a/pulumitest/copy.go +++ b/pulumitest/copy.go @@ -21,7 +21,7 @@ func (a *PulumiTest) CopyToTempDir(opts ...opttest.Option) *PulumiTest { destination := filepath.Join(tempDir, sourceBase) err := os.Mkdir(destination, 0755) if err != nil { - a.t.Fatal(err) + a.fatal(err) } return a.CopyTo(destination, opts...) @@ -34,7 +34,7 @@ func (a *PulumiTest) CopyTo(dir string, opts ...opttest.Option) *PulumiTest { err := copyDirectory(a.source, dir) if err != nil { - a.t.Fatal(err) + a.fatal(err) } options := a.options.Copy() diff --git a/pulumitest/destroy.go b/pulumitest/destroy.go index 33a2764..68ffcde 100644 --- a/pulumitest/destroy.go +++ b/pulumitest/destroy.go @@ -11,14 +11,14 @@ func (a *PulumiTest) Destroy(opts ...optdestroy.Option) auto.DestroyResult { a.t.Log("destroying") if a.currentStack == nil { - a.t.Fatal("no current stack") + a.fatal("no current stack") } if !a.options.DisableGrpcLog { a.ClearGrpcLog() } result, err := a.currentStack.Destroy(a.ctx, opts...) if err != nil { - a.t.Fatalf("failed to destroy: %s", err) + a.fatalf("failed to destroy: %s", err) } return result } diff --git a/pulumitest/exportStack.go b/pulumitest/exportStack.go index 95b7b82..39a4a6a 100644 --- a/pulumitest/exportStack.go +++ b/pulumitest/exportStack.go @@ -10,11 +10,11 @@ func (a *PulumiTest) ExportStack() apitype.UntypedDeployment { a.t.Log("exporting stack") if a.currentStack == nil { - a.t.Fatal("no current stack") + a.fatal("no current stack") } out, err := a.currentStack.Workspace().ExportStack(a.Context(), a.currentStack.Name()) if err != nil { - a.t.Fatalf("failed to export stack: %s", err) + a.fatalf("failed to export stack: %s", err) } return out } diff --git a/pulumitest/grpcLog.go b/pulumitest/grpcLog.go index f1bf1a6..2b1068e 100644 --- a/pulumitest/grpcLog.go +++ b/pulumitest/grpcLog.go @@ -23,7 +23,7 @@ func (pt *PulumiTest) GrpcLog() *grpclog.GrpcLog { log, err := grpclog.LoadLog(env["PULUMI_DEBUG_GRPC"]) if err != nil { - pt.t.Fatalf("failed to load grpc log: %s", err) + pt.fatalf("failed to load grpc log: %s", err) } return log } @@ -35,6 +35,6 @@ func (pt *PulumiTest) ClearGrpcLog() { return } if err := os.RemoveAll(env["PULUMI_DEBUG_GRPC"]); err != nil { - pt.t.Fatalf("failed to clear gRPC log: %s", err) + pt.fatalf("failed to clear gRPC log: %s", err) } } diff --git a/pulumitest/importStack.go b/pulumitest/importStack.go index 7467452..10c09bf 100644 --- a/pulumitest/importStack.go +++ b/pulumitest/importStack.go @@ -10,10 +10,10 @@ func (a *PulumiTest) ImportStack(source apitype.UntypedDeployment) { a.t.Log("importing stack") if a.currentStack == nil { - a.t.Fatal("no current stack") + a.fatal("no current stack") } err := a.currentStack.Workspace().ImportStack(a.Context(), a.currentStack.Name(), source) if err != nil { - a.t.Fatalf("failed to import stack: %s", err) + a.fatalf("failed to import stack: %s", err) } } diff --git a/pulumitest/install.go b/pulumitest/install.go index aa91d27..56a3575 100644 --- a/pulumitest/install.go +++ b/pulumitest/install.go @@ -13,7 +13,7 @@ func (a *PulumiTest) Install() string { cmd.Dir = a.source out, err := cmd.CombinedOutput() if err != nil { - a.t.Fatalf("failed to install packages and plugins: %s\n%s", err, out) + a.fatalf("failed to install packages and plugins: %s\n%s", err, out) } return string(out) } diff --git a/pulumitest/newStack.go b/pulumitest/newStack.go index 3763499..ab4f333 100644 --- a/pulumitest/newStack.go +++ b/pulumitest/newStack.go @@ -59,7 +59,7 @@ func (pt *PulumiTest) NewStack(stackName string, opts ...optnewstack.NewStackOpt providerPorts, err := providers.StartProviders(providerContext, providerFactories, pt) if err != nil { cancelProviders() - pt.t.Fatalf("failed to start providers: %v", err) + pt.fatalf("failed to start providers: %v", err) } else { pt.t.Cleanup(func() { cancelProviders() @@ -79,14 +79,14 @@ func (pt *PulumiTest) NewStack(stackName string, opts ...optnewstack.NewStackOpt stackOpts = append(stackOpts, options.ExtraWorkspaceOptions...) stackOpts = append(stackOpts, stackOptions.Opts...) - pt.T().Logf("creating stack %s", stackName) + pt.logf("creating stack %s", stackName) stack, err := auto.NewStackLocalSource(pt.ctx, stackName, pt.source, stackOpts...) providerPluginPaths := options.ProviderPluginPaths() if len(providerPluginPaths) > 0 { projectSettings, err := stack.Workspace().ProjectSettings(pt.ctx) if err != nil { - pt.t.Fatalf("failed to get project settings: %s", err) + pt.fatalf("failed to get project settings: %s", err) } var plugins workspace.Plugins if projectSettings.Plugins != nil { @@ -103,7 +103,7 @@ func (pt *PulumiTest) NewStack(stackName string, opts ...optnewstack.NewStackOpt relPath := providerPluginPaths[providers.ProviderName(name)] absPath, err := filepath.Abs(relPath) if err != nil { - pt.t.Fatalf("failed to get absolute path for %s: %s", relPath, err) + pt.fatalf("failed to get absolute path for %s: %s", relPath, err) } found := false @@ -125,7 +125,7 @@ func (pt *PulumiTest) NewStack(stackName string, opts ...optnewstack.NewStackOpt projectSettings.Plugins = &plugins err = stack.Workspace().SaveProjectSettings(pt.ctx, projectSettings) if err != nil { - pt.t.Fatalf("failed to save project settings: %s", err) + pt.fatalf("failed to save project settings: %s", err) } } @@ -133,10 +133,10 @@ func (pt *PulumiTest) NewStack(stackName string, opts ...optnewstack.NewStackOpt for _, pkg := range options.YarnLinks { cmd := exec.Command("yarn", "link", pkg) cmd.Dir = pt.source - pt.t.Logf("linking yarn package: %s", cmd) + pt.logf("linking yarn package: %s", cmd) out, err := cmd.CombinedOutput() if err != nil { - pt.t.Fatalf("failed to link yarn package %s: %s\n%s", pkg, err, out) + pt.fatalf("failed to link yarn package %s: %s\n%s", pkg, err, out) } } } @@ -151,21 +151,21 @@ func (pt *PulumiTest) NewStack(stackName string, opts ...optnewstack.NewStackOpt relPath := options.GoModReplacements[old] absPath, err := filepath.Abs(relPath) if err != nil { - pt.t.Fatalf("failed to get absolute path for %s: %s", relPath, err) + pt.fatalf("failed to get absolute path for %s: %s", relPath, err) } replacement := fmt.Sprintf("%s=%s", old, absPath) cmd := exec.Command("go", "mod", "edit", "-replace", replacement) cmd.Dir = pt.source - pt.t.Logf("adding go.mod replacement: %s", cmd) + pt.logf("adding go.mod replacement: %s", cmd) out, err := cmd.CombinedOutput() if err != nil { - pt.t.Fatalf("failed to add go.mod replacement %s: %s\n%s", replacement, err, out) + pt.fatalf("failed to add go.mod replacement %s: %s\n%s", replacement, err, out) } } } if err != nil { - pt.t.Fatalf("failed to create stack: %s", err) + pt.fatalf("failed to create stack: %s", err) return nil } if !stackOptions.SkipDestroy { @@ -174,11 +174,11 @@ func (pt *PulumiTest) NewStack(stackName string, opts ...optnewstack.NewStackOpt pt.t.Log("cleaning up stack") _, err := stack.Destroy(pt.ctx) if err != nil { - pt.t.Errorf("failed to destroy stack: %s", err) + pt.errorf("failed to destroy stack: %s", err) } err = stack.Workspace().RemoveStack(pt.ctx, stackName, optremove.Force()) if err != nil { - pt.t.Errorf("failed to remove stack: %s", err) + pt.errorf("failed to remove stack: %s", err) } }) } diff --git a/pulumitest/preview.go b/pulumitest/preview.go index ac065b1..a1e0e10 100644 --- a/pulumitest/preview.go +++ b/pulumitest/preview.go @@ -11,14 +11,14 @@ func (a *PulumiTest) Preview(opts ...optpreview.Option) auto.PreviewResult { a.t.Log("previewing update") if a.currentStack == nil { - a.t.Fatal("no current stack") + a.fatal("no current stack") } if !a.options.DisableGrpcLog { a.ClearGrpcLog() } result, err := a.currentStack.Preview(a.ctx, opts...) if err != nil { - a.t.Fatalf("failed to preview update: %s", err) + a.fatalf("failed to preview update: %s", err) } return result } diff --git a/pulumitest/pulumiTest.go b/pulumitest/pulumiTest.go index d483803..1ca4865 100644 --- a/pulumitest/pulumiTest.go +++ b/pulumitest/pulumiTest.go @@ -2,14 +2,14 @@ package pulumitest import ( "context" - "testing" + "fmt" "github.com/pulumi/providertest/pulumitest/opttest" "github.com/pulumi/pulumi/sdk/v3/go/auto" ) type PulumiTest struct { - t *testing.T + t PT ctx context.Context source string options *opttest.Options @@ -21,7 +21,7 @@ type PulumiTest struct { // 1. Copy the source to a temporary directory. // 2. Install dependencies. // 3. Create a new stack called "test" with state stored to a local temporary directory and a fixed passphrase for encryption. -func NewPulumiTest(t *testing.T, source string, opts ...opttest.Option) *PulumiTest { +func NewPulumiTest(t PT, source string, opts ...opttest.Option) *PulumiTest { t.Helper() ctx := testContext(t) options := opttest.DefaultOptions() @@ -42,7 +42,7 @@ func NewPulumiTest(t *testing.T, source string, opts ...opttest.Option) *PulumiT return pt } -func testContext(t *testing.T) context.Context { +func testContext(t PT) context.Context { t.Helper() var ctx context.Context var cancel context.CancelFunc @@ -71,11 +71,6 @@ func (a *PulumiTest) Source() string { return a.source } -// T returns the current testing.T instance. -func (a *PulumiTest) T() *testing.T { - return a.t -} - // Context returns the current context.Context instance used for automation API calls. func (a *PulumiTest) Context() context.Context { return a.ctx @@ -85,3 +80,31 @@ func (a *PulumiTest) Context() context.Context { func (a *PulumiTest) CurrentStack() *auto.Stack { return a.currentStack } + +func (a *PulumiTest) logf(format string, args ...any) { + a.t.Log(fmt.Sprintf(format, args...)) +} + +func (a *PulumiTest) log(args ...any) { + a.t.Log(args...) +} + +func (a *PulumiTest) errorf(format string, args ...any) { + a.t.Log(fmt.Sprintf(format, args...)) + a.t.Fail() +} + +func (a *PulumiTest) error(args ...any) { + a.t.Log(args...) + a.t.Fail() +} + +func (a *PulumiTest) fatalf(format string, args ...any) { + a.t.Log(fmt.Sprintf(format, args...)) + a.t.FailNow() +} + +func (a *PulumiTest) fatal(args ...any) { + a.t.Log(args...) + a.t.FailNow() +} diff --git a/pulumitest/refresh.go b/pulumitest/refresh.go index 63dd20e..3342ac4 100644 --- a/pulumitest/refresh.go +++ b/pulumitest/refresh.go @@ -11,14 +11,14 @@ func (a *PulumiTest) Refresh(opts ...optrefresh.Option) auto.RefreshResult { a.t.Log("refreshing") if a.currentStack == nil { - a.t.Fatal("no current stack") + a.fatal("no current stack") } if !a.options.DisableGrpcLog { a.ClearGrpcLog() } result, err := a.currentStack.Refresh(a.ctx, opts...) if err != nil { - a.t.Fatalf("failed to refresh: %s", err) + a.fatalf("failed to refresh: %s", err) } return result } diff --git a/pulumitest/run.go b/pulumitest/run.go index 613f4dd..73eb47c 100644 --- a/pulumitest/run.go +++ b/pulumitest/run.go @@ -16,7 +16,7 @@ import ( // WithCache can be used to skip executing the run and return the cached stack state if available, or to cache the stack state after executing the run. // Options will be inherited from the original test, but can be added to with `optrun.WithOpts` or reset with `opttest.Defaults()`. func (pulumiTest *PulumiTest) Run(execute func(test *PulumiTest), opts ...optrun.Option) *PulumiTest { - pulumiTest.T().Helper() + pulumiTest.t.Helper() options := optrun.DefaultOptions() for _, o := range opts { @@ -28,12 +28,12 @@ func (pulumiTest *PulumiTest) Run(execute func(test *PulumiTest), opts ...optrun if options.EnableCache { stackExport, err = tryReadStackExport(options.CachePath) if err != nil { - pulumiTest.T().Fatalf("failed to read stack export: %v", err) + pulumiTest.fatalf("failed to read stack export: %v", err) } if stackExport != nil { - pulumiTest.T().Logf("run cache found at %s", options.CachePath) + pulumiTest.logf("run cache found at %s", options.CachePath) } else { - pulumiTest.T().Logf("no run cache found at %s", options.CachePath) + pulumiTest.logf("no run cache found at %s", options.CachePath) } } @@ -42,10 +42,10 @@ func (pulumiTest *PulumiTest) Run(execute func(test *PulumiTest), opts ...optrun execute(isolatedTest) exportedStack := isolatedTest.ExportStack() if options.EnableCache { - isolatedTest.T().Logf("writing stack state to %s", options.CachePath) + isolatedTest.logf("writing stack state to %s", options.CachePath) err = writeStackExport(options.CachePath, &exportedStack, false /* overwrite */) if err != nil { - isolatedTest.T().Fatalf("failed to write snapshot to %s: %v", options.CachePath, err) + isolatedTest.fatalf("failed to write snapshot to %s: %v", options.CachePath, err) } } stackExport = &exportedStack @@ -55,13 +55,13 @@ func (pulumiTest *PulumiTest) Run(execute func(test *PulumiTest), opts ...optrun stackName := pulumiTest.CurrentStack().Name() fixedStack, err := fixupStackName(stackExport, stackName) if err != nil { - pulumiTest.T().Fatalf("failed to fixup stack name: %v", err) + pulumiTest.fatalf("failed to fixup stack name: %v", err) } if fixedStack != stackExport { - pulumiTest.T().Logf("updating snapshot with fixed stack name: %s", stackName) + pulumiTest.logf("updating snapshot with fixed stack name: %s", stackName) err = writeStackExport(options.CachePath, fixedStack, true /* overwrite */) if err != nil { - pulumiTest.T().Fatalf("failed to write snapshot to %s: %v", options.CachePath, err) + pulumiTest.fatalf("failed to write snapshot to %s: %v", options.CachePath, err) } stackExport = fixedStack } diff --git a/pulumitest/setConfig.go b/pulumitest/setConfig.go index 9033084..fae6b37 100644 --- a/pulumitest/setConfig.go +++ b/pulumitest/setConfig.go @@ -6,10 +6,10 @@ func (a *PulumiTest) SetConfig(key, value string) { a.t.Helper() if a.currentStack == nil { - a.t.Fatal("no current stack") + a.fatal("no current stack") } err := a.currentStack.SetConfig(a.ctx, key, auto.ConfigValue{Value: value}) if err != nil { - a.t.Fatalf("failed to set config: %s", err) + a.fatalf("failed to set config: %s", err) } } diff --git a/pulumitest/testingT.go b/pulumitest/testingT.go new file mode 100644 index 0000000..0368d70 --- /dev/null +++ b/pulumitest/testingT.go @@ -0,0 +1,16 @@ +package pulumitest + +import ( + "time" +) + +// A subset of *testing.T functionality used by pulumitest. +type PT interface { + TempDir() string + Log(...any) + Fail() + FailNow() + Cleanup(func()) + Helper() + Deadline() (time.Time, bool) +} diff --git a/pulumitest/up.go b/pulumitest/up.go index 516d3f5..8f3445d 100644 --- a/pulumitest/up.go +++ b/pulumitest/up.go @@ -11,14 +11,14 @@ func (a *PulumiTest) Up(opts ...optup.Option) auto.UpResult { a.t.Log("deploying") if a.currentStack == nil { - a.t.Fatal("no current stack") + a.fatal("no current stack") } if !a.options.DisableGrpcLog { a.ClearGrpcLog() } result, err := a.currentStack.Up(a.ctx, opts...) if err != nil { - a.t.Fatalf("failed to deploy: %s", err) + a.fatalf("failed to deploy: %s", err) } return result } diff --git a/pulumitest/updateSource.go b/pulumitest/updateSource.go index 5567fdb..ebf2012 100644 --- a/pulumitest/updateSource.go +++ b/pulumitest/updateSource.go @@ -7,6 +7,6 @@ func (a *PulumiTest) UpdateSource(pathElems ...string) { a.t.Helper() path := filepath.Join(pathElems...) - a.t.Logf("updating source from %s", path) + a.logf("updating source from %s", path) copyDirectory(path, a.source) }