Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Testing

The SDK ships a fake package that provides an in-memory implementation of all client interfaces. Use it in your test suites to exercise SDK interactions without a real gateway.

import "github.com/rhuss/openshell-sdk-go/openshell/v1/fake"

The fake client follows the same pattern as k8s.io/client-go/kubernetes/fake: it maintains in-memory stores, supports watch event broadcasting, and returns the same StatusError codes as the real client.

Creating a Fake Client

func TestMyOperator(t *testing.T) {
    client := fake.NewClient()
    defer client.Close()

    ctx := context.Background()

    // Use client exactly like the real SDK
    sb, err := client.Sandboxes().Create(ctx, "test-sandbox", &v1.SandboxSpec{}, nil)
    require.NoError(t, err)
    assert.Equal(t, "Provisioning", string(sb.Status.Phase))
}

The returned *fake.Client satisfies v1.ClientInterface, so you can pass it anywhere your code accepts the interface.

Fixture Seeding

Pre-populate the fake client with existing resources before your test runs. Seeded resources are available immediately via Get and List without going through Create.

AddSandbox

client := fake.NewClient()

// Pre-seed a sandbox that already exists
client.AddSandbox(&types.Sandbox{
    Name: "existing-sandbox",
    Status: types.SandboxStatus{
        Phase: types.SandboxReady,
    },
    ResourceVersion: 5,
})

// Now Get returns it immediately
sb, err := client.Sandboxes().Get(ctx, "existing-sandbox")
// sb.Status.Phase == "Ready"

AddProvider

client := fake.NewClient()

// Pre-seed a provider
client.AddProvider(&types.Provider{
    Name: "my-openai",
    Type: "openai",
    Spec: types.ProviderSpec{
        Credentials: map[string]string{
            "api_key": "sk-test-key",
        },
    },
})

// List returns the seeded provider
providers, _ := client.Providers().List(ctx)
// len(providers) == 1

Sandbox Lifecycle

The fake client implements the full sandbox lifecycle. Created sandboxes start in the Provisioning phase. Calling WaitReady transitions them to Ready synchronously.

client := fake.NewClient()
ctx := context.Background()

// Create starts in Provisioning
sb, err := client.Sandboxes().Create(ctx, "my-sandbox", &v1.SandboxSpec{}, nil)
assert.Equal(t, types.SandboxProvisioning, sb.Status.Phase)

// WaitReady transitions to Ready (synchronous in fake)
sb, err = client.Sandboxes().WaitReady(ctx, "my-sandbox")
assert.Equal(t, types.SandboxReady, sb.Status.Phase)

// Delete removes the sandbox
err = client.Sandboxes().Delete(ctx, "my-sandbox")
assert.NoError(t, err)

// Get after delete returns NotFound
_, err = client.Sandboxes().Get(ctx, "my-sandbox")
assert.True(t, v1.IsNotFound(err))

Watch Events

The fake client broadcasts watch events when resources change. Use watchers to test event-driven code.

client := fake.NewClient()
ctx := context.Background()

// Start watching before making changes
watcher, err := client.Sandboxes().Watch(ctx, "my-sandbox")
require.NoError(t, err)
defer watcher.Stop()

// Create a sandbox — triggers an ADDED event
client.Sandboxes().Create(ctx, "my-sandbox", &v1.SandboxSpec{}, nil)

// Read the event from the channel
event := <-watcher.ResultChan()
assert.Equal(t, types.EventAdded, event.Type)
assert.Equal(t, "my-sandbox", event.Object.Name)

StopOnTerminal

Setting StopOnTerminal: true causes the watcher to close automatically when the sandbox reaches a terminal phase (Ready or Error).

watcher, err := client.Sandboxes().Watch(ctx, "my-sandbox", v1.WatchOptions{
    StopOnTerminal: true,
})
require.NoError(t, err)

// Create and transition to Ready
client.Sandboxes().Create(ctx, "my-sandbox", &v1.SandboxSpec{}, nil)
client.Sandboxes().WaitReady(ctx, "my-sandbox")

// Drain events — channel closes after the Ready event
var events []types.Event[*types.Sandbox]
for ev := range watcher.ResultChan() {
    events = append(events, ev)
}
// Channel is now closed

Health Simulation

Use WithHealthResult to simulate an unhealthy or degraded gateway.

// Default: healthy gateway
client := fake.NewClient()
result, _ := client.Health().Check(ctx)
// result.Healthy == true, result.Version == "fake"

// Simulate unhealthy gateway
client = fake.NewClient(fake.WithHealthResult(&types.HealthResult{
    Healthy: false,
    Version: "1.2.3",
}))
result, _ = client.Health().Check(ctx)
// result.Healthy == false

Error Behavior

The fake client returns the same StatusError codes as the real client:

ScenarioError Code
Get for a non-existent resourceErrorNotFound
Create with a duplicate nameErrorAlreadyExists
Any call after Close()ErrorUnavailable
Unimplemented operations (e.g., GetLogs)ErrorUnimplemented
client := fake.NewClient()
client.Close()

_, err := client.Sandboxes().Get(ctx, "anything")
assert.True(t, v1.IsUnavailable(err))

Concurrency

All fake client operations are safe for concurrent use. The internal stores use mutex-based synchronization. This means you can safely use the fake client from multiple goroutines in parallel tests.

See also: Error Handling, API Reference