stack

package
v0.0.0-...-f2ecac5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 35 Imported by: 0

Documentation

Overview

Package stack provides a simple API for spinning up the complete Forge network in Go tests using testcontainers-go.

Example usage:

func TestUploadFlow(t *testing.T) {
    s := stack.MustNewStack(t,
        stack.WithPiriImage("my-piri:test"),
    )

    resp, _ := http.Get(s.PiriEndpointN(0) + "/readyz")
    assert.Equal(t, 200, resp.StatusCode)
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildDelegatorImage

func BuildDelegatorImage(t *testing.T, repoPath string) string

BuildDelegatorImage builds the delegator from a local repo and returns the image tag. The image is automatically cleaned up when the test completes.

func BuildGuppyImage

func BuildGuppyImage(t *testing.T, repoPath string) string

BuildGuppyImage builds guppy from a local repo and returns the image tag. The image is automatically cleaned up when the test completes.

Example:

func TestWithLocalGuppy(t *testing.T) {
    localGuppy := stack.BuildGuppyImage(t, "..")
    s := stack.MustNewStack(t, stack.WithGuppyImage(localGuppy))
}

func BuildImage

func BuildImage(t *testing.T, repoPath string, imageName string) string

BuildImage builds a Docker image from the repo's Dockerfile. Returns the image tag. The image is automatically cleaned up when the test completes.

This enables testing local code changes against the full smelt stack:

func TestWithLocalChanges(t *testing.T) {
    localPiri := stack.BuildImage(t, "..", "local-piri")
    s := stack.MustNewStack(t, stack.WithPiriImage(localPiri))
    // ... test against local changes
}

func BuildIndexerImage

func BuildIndexerImage(t *testing.T, repoPath string) string

BuildIndexerImage builds the indexing-service from a local repo and returns the image tag. The image is automatically cleaned up when the test completes.

func BuildPiriImage

func BuildPiriImage(t *testing.T, repoPath string) string

BuildPiriImage builds piri from a local repo and returns the image tag. The image is automatically cleaned up when the test completes.

Example:

func TestWithLocalPiri(t *testing.T) {
    localPiri := stack.BuildPiriImage(t, "..") // parent dir is repo root
    s := stack.MustNewStack(t, stack.WithPiriImage(localPiri))
}

func BuildUploadImage

func BuildUploadImage(t *testing.T, repoPath string) string

BuildUploadImage builds the upload service from a local repo and returns the image tag. The image is automatically cleaned up when the test completes.

func CleanupLeaked

func CleanupLeaked(ctx context.Context) error

CleanupLeaked removes containers and volumes left behind by prior pkg/stack test runs that didn't tear down cleanly (SIGKILL, panic, `keepOnFailure` without manual cleanup, oom-killed test binary, etc.). Call it from TestMain before running tests to start from a clean slate:

func TestMain(m *testing.M) {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
    if err := stack.CleanupLeaked(ctx); err != nil {
        log.Printf("cleanup warning: %v", err)
    }
    cancel()
    os.Exit(m.Run())
}

Only removes resources whose names start with "smeltery-" (the compose project prefix used by NewStack). Does not touch the shared `forge-network` since it's reused across test runs, and does not interfere with a parallel test suite running against the same host — unless that suite ALSO uses pkg/stack and happens to be live when CleanupLeaked runs. For single-suite test machines this is safe.

func ListEmbeddedSnapshots

func ListEmbeddedSnapshots() ([]string, error)

ListEmbeddedSnapshots returns the names of snapshots bundled with this smelt module. Each name is usable as the argument to WithEmbeddedSnapshot.

External consumers (packages that import smelt) should call this rather than hunting for snapshot paths on disk — paths into the smelt repo don't exist in a consumer's checkout.

Types

type Option

type Option func(*config)

Option configures a Stack.

func WithBlockchainImage

func WithBlockchainImage(image string) Option

WithBlockchainImage sets the blockchain (Anvil) container image.

func WithDelegatorImage

func WithDelegatorImage(image string) Option

WithDelegatorImage sets the delegator container image.

func WithEmbeddedSnapshot

func WithEmbeddedSnapshot(name string) Option

WithEmbeddedSnapshot boots the stack from a snapshot bundled with the smelt Go module. This is the recommended path for external consumers — the snapshot travels with the Go import, so there are no filesystem paths to reason about:

s := stack.MustNewStack(t,
    stack.WithEmbeddedSnapshot("3-piri-filesystem-sqlite"),
)

Discover available names at runtime via stack.ListEmbeddedSnapshots(). Incompatible with WithSnapshot, WithPiriCount, WithPiriNodes. The SMELT_TEST_NO_SNAPSHOT env-var skip pattern applies the same as with WithSnapshot.

func WithGuppyImage

func WithGuppyImage(image string) Option

WithGuppyImage sets the guppy container image.

func WithIPNIImage

func WithIPNIImage(image string) Option

WithIPNIImage sets the IPNI container image.

func WithIndexerImage

func WithIndexerImage(image string) Option

WithIndexerImage sets the indexer container image.

func WithIngotImage

func WithIngotImage(image string) Option

WithIngotImage sets the ingot container image.

func WithKeepOnFailure

func WithKeepOnFailure() Option

WithKeepOnFailure prevents cleanup when a test fails, useful for debugging.

func WithPiriBinary

func WithPiriBinary(path string) Option

WithPiriBinary mounts a local piri binary into the piri container(s), replacing the image's binary for fast iteration. The binary must be compiled for linux/amd64. Equivalent to WithServiceBinary("piri", path).

func WithPiriCount

func WithPiriCount(n int) Option

WithPiriCount configures N identical piri nodes with default storage settings.

Example:

s := stack.MustNewStack(t, stack.WithPiriCount(3))

func WithPiriImage

func WithPiriImage(image string) Option

WithPiriImage sets the piri container image.

func WithPiriNodes

func WithPiriNodes(nodes ...PiriNodeConfig) Option

WithPiriNodes configures specific piri nodes with individual settings.

Example:

s := stack.MustNewStack(t, stack.WithPiriNodes(
    stack.PiriNodeConfig{Postgres: true, S3: true},
    stack.PiriNodeConfig{},
))

func WithServiceBinary

func WithServiceBinary(service, path string) Option

WithServiceBinary mounts a prebuilt binary over a service's binary in the container, replacing the image's copy without rebuilding the image. The binary must be a static linux/amd64 build. service is a smelt service name (piri, upload, signing-service, indexer, delegator, guppy).

For building from a local checkout via the Go workspace, prefer WithWorkspaceBinaries; this option is the explicit, prebuilt-binary escape hatch.

func WithServiceConfig

func WithServiceConfig(service, path string) Option

WithServiceConfig mounts a test-provided config file over a service's in-container config path (e.g. ingot's /etc/ingot/config.yaml), replacing the default that ships with smelt's system definition. This lets a service repo's e2e tests exercise config changes without a smelt release. Only services with a registered config path support this (see pkg/workspace); NewStack errors for others.

func WithSignerImage

func WithSignerImage(image string) Option

WithSignerImage sets the signing service container image.

func WithSnapshot

func WithSnapshot(path string) Option

WithSnapshot boots the stack from a saved snapshot at a filesystem path. Use this for snapshots living outside the smelt module (e.g. ones you saved yourself via `smelt snapshot save`, or extras committed alongside your tests). For consumers of smelt as a Go dependency, prefer WithEmbeddedSnapshot — filesystem paths into the smelt repo don't exist in a consumer's checkout.

The snapshot directory must contain the layout produced by `smelt snapshot save` (manifest.json, smelt.yml, blockchain/, keys/, proofs/, volumes/). Topology comes from the snapshot's embedded smelt.yml — pairing with WithPiriCount or WithPiriNodes returns an error from NewStack. Image references in your .env must match what the snapshot was saved against (the Go SDK does not emit a drift warning; that's the test author's responsibility).

CI should exercise the cold-boot path. Skip in CI via an env check:

var opts []stack.Option
if os.Getenv("SMELT_TEST_NO_SNAPSHOT") == "" {
    opts = append(opts, stack.WithSnapshot("../../snapshots/3-piri-filesystem-sqlite"))
}
s := stack.MustNewStack(t, opts...)

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the maximum time to wait for the stack to start.

func WithUploadImage

func WithUploadImage(image string) Option

WithUploadImage sets the upload service container image.

func WithWorkspaceBinaries

func WithWorkspaceBinaries() Option

WithWorkspaceBinaries builds every service selected by the active Go workspace (go.work) from local sibling source and mounts the resulting binaries over the published images. Selection follows the use-list: a service is built when its module is listed; if libforge is listed, all services are rebuilt. Requires an active go.work (see pkg/workspace).

Example:

s := stack.MustNewStack(t, stack.WithWorkspaceBinaries())

type PiriNodeConfig

type PiriNodeConfig struct {
	Postgres bool // Use PostgreSQL for this node
	S3       bool // Use S3 for this node
}

PiriNodeConfig configures a single piri node in the test stack.

type Stack

type Stack struct {
	// contains filtered or unexported fields
}

Stack represents a running Forge network.

func MustNewStack

func MustNewStack(t *testing.T, opts ...Option) *Stack

MustNewStack creates and starts a network, calling t.Fatal on error.

func NewStack

func NewStack(ctx context.Context, t *testing.T, opts ...Option) (*Stack, error)

NewStack creates and starts a complete Forge network. Returns error if startup fails. Cleanup is automatically registered via t.Cleanup().

func (*Stack) Close

func (s *Stack) Close(ctx context.Context) error

Close shuts down the stack and cleans up resources. This is called automatically via t.Cleanup(), but can be called manually.

func (*Stack) EmailEndpoint

func (s *Stack) EmailEndpoint() string

EmailEndpoint returns the HTTP API endpoint for the email service.

func (*Stack) Exec

func (s *Stack) Exec(ctx context.Context, service string, args ...string) (stdout, stderr string, err error)

Exec executes a command inside a service container and returns stdout and stderr separately.

func (*Stack) IngotEndpoint

func (s *Stack) IngotEndpoint() string

IngotEndpoint returns the host S3 endpoint for the ingot gateway (container port 9000). The host is normalized to 127.0.0.1 rather than "localhost": the AWS SDK uses virtual-host-style addressing (bucket.host) for a hostname endpoint but path-style for an IP, and ingot's versitygw front end is path-style — a hostname endpoint yields 405 MethodNotAllowed on CreateBucket.

func (*Stack) Logs

func (s *Stack) Logs(ctx context.Context, service string) (string, error)

Logs returns the logs for a service.

func (*Stack) PiriCount

func (s *Stack) PiriCount() int

PiriCount returns the number of piri nodes in the stack.

func (*Stack) PiriEndpointN

func (s *Stack) PiriEndpointN(index int) string

PiriEndpointN returns the HTTP endpoint for the Nth piri node.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL