From 0605b3e9474620acf9b108cdc9da844ea95dced4 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Fri, 21 Aug 2026 15:44:59 -0400 Subject: [PATCH 1/2] feat(testutil): SDK-fake-backed openshell.Client + FakeFactory (PR1 S4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add internal/testutil with NewFake/NewFakeClient returning a real openshell.Client backed by the OpenShell SDK fake (via sdkclient.NewFromClient, so tests exercise the real mapping/translation), and FakeFactory adapting a fixed Client to openshell.Factory. Self-tests round-trip Health + Providers through the real mapping and confirm FakeFactory returns the injected client. SDK fake types stay confined to testutil/sdkclient — the internal/openshell firewall is intact. --- internal/testutil/fake_platform.go | 40 +++++++++++++ internal/testutil/fake_platform_test.go | 80 +++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 internal/testutil/fake_platform.go create mode 100644 internal/testutil/fake_platform_test.go diff --git a/internal/testutil/fake_platform.go b/internal/testutil/fake_platform.go new file mode 100644 index 0000000..c45f0ca --- /dev/null +++ b/internal/testutil/fake_platform.go @@ -0,0 +1,40 @@ +// Package testutil provides test utilities for exercising the harness layer +// against the OpenShell Go SDK fake, which validates the real sdkclient +// mapping/translation without hitting a live gateway. +package testutil + +import ( + "context" + + fake "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/fake" + + "github.com/stackrox/harness-openshell/internal/openshell" + "github.com/stackrox/harness-openshell/internal/openshell/sdkclient" +) + +// NewFake returns an openshell.Client backed by the SDK fake, exercising the +// REAL sdkclient mapping/translation. Seed the fake via fake.With* options +// before construction. For tests that need to call fake.Client.AddProvider +// after construction, use NewFakeClient instead. +func NewFake(workspace string, opts ...fake.ClientOption) openshell.Client { + c, _ := NewFakeClient(workspace, opts...) + return c +} + +// NewFakeClient returns an openshell.Client backed by the SDK fake and the +// underlying *fake.Client for direct test manipulation. This allows tests to +// call fake.Client.AddProvider on the returned *fake.Client after construction. +// Implement NewFake in terms of this to avoid duplication. +func NewFakeClient(workspace string, opts ...fake.ClientOption) (openshell.Client, *fake.Client) { + raw := fake.NewClient(opts...) + return sdkclient.NewFromClient(raw, workspace), raw +} + +// FakeFactory returns a Factory closure that ignores its context and Target +// arguments and always returns the given Client and nil error. Use this to +// wire a test client into code that depends on the Factory seam. +func FakeFactory(c openshell.Client) openshell.Factory { + return func(context.Context, openshell.Target) (openshell.Client, error) { + return c, nil + } +} diff --git a/internal/testutil/fake_platform_test.go b/internal/testutil/fake_platform_test.go new file mode 100644 index 0000000..5c1ce37 --- /dev/null +++ b/internal/testutil/fake_platform_test.go @@ -0,0 +1,80 @@ +package testutil + +import ( + "context" + "testing" + + fake "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/fake" + "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" + + "github.com/stackrox/harness-openshell/internal/openshell" +) + +func TestHealthRoundTrip(t *testing.T) { + ctx := context.Background() + c := NewFake("default", fake.WithHealthResult(&types.HealthResult{ + Healthy: true, + Version: "1.2.3", + })) + + h, err := c.Health(ctx) + if err != nil { + t.Fatalf("Health() returned unexpected error: %v", err) + } + if !h.Healthy { + t.Errorf("expected Healthy=true, got %v", h.Healthy) + } + if h.Version != "1.2.3" { + t.Errorf("expected Version=%q, got %q", "1.2.3", h.Version) + } +} + +func TestProvidersRoundTrip(t *testing.T) { + ctx := context.Background() + c, raw := NewFakeClient("default") + raw.AddProvider("default", &types.Provider{ + Name: "p1", + Type: "openai", + }) + + providers, err := c.Providers(ctx) + if err != nil { + t.Fatalf("Providers() returned unexpected error: %v", err) + } + if len(providers) != 1 { + t.Errorf("expected 1 provider, got %d", len(providers)) + } + if providers[0].Name != "p1" { + t.Errorf("expected Name=%q, got %q", "p1", providers[0].Name) + } + if providers[0].Type != "openai" { + t.Errorf("expected Type=%q, got %q", "openai", providers[0].Type) + } +} + +func TestEmptyProviders(t *testing.T) { + ctx := context.Background() + c := NewFake("default") + + providers, err := c.Providers(ctx) + if err != nil { + t.Fatalf("Providers() returned unexpected error: %v", err) + } + if len(providers) != 0 { + t.Errorf("expected 0 providers, got %d", len(providers)) + } +} + +func TestFakeFactory(t *testing.T) { + ctx := context.Background() + c := NewFake("default") + f := FakeFactory(c) + + got, err := f(ctx, openshell.Target{Gateway: "anything", Workspace: "x"}) + if err != nil { + t.Errorf("FakeFactory returned unexpected error: %v", err) + } + if got != c { + t.Errorf("expected returned client to be the same as input") + } +} From 4acb3eb9829738ccf7c650c2f97160fc542ccf56 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Fri, 21 Aug 2026 19:34:07 -0400 Subject: [PATCH 2/2] test(testutil): fail fast on unexpected provider count Use t.Fatalf instead of t.Errorf on the provider-count assertion so the test stops before indexing providers[0], avoiding a panic when the slice is empty. (CodeRabbit PR #92) --- internal/testutil/fake_platform_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testutil/fake_platform_test.go b/internal/testutil/fake_platform_test.go index 5c1ce37..fa8174c 100644 --- a/internal/testutil/fake_platform_test.go +++ b/internal/testutil/fake_platform_test.go @@ -42,7 +42,7 @@ func TestProvidersRoundTrip(t *testing.T) { t.Fatalf("Providers() returned unexpected error: %v", err) } if len(providers) != 1 { - t.Errorf("expected 1 provider, got %d", len(providers)) + t.Fatalf("expected 1 provider, got %d", len(providers)) } if providers[0].Name != "p1" { t.Errorf("expected Name=%q, got %q", "p1", providers[0].Name)