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 ¶
- func BuildDelegatorImage(t *testing.T, repoPath string) string
- func BuildGuppyImage(t *testing.T, repoPath string) string
- func BuildImage(t *testing.T, repoPath string, imageName string) string
- func BuildIndexerImage(t *testing.T, repoPath string) string
- func BuildPiriImage(t *testing.T, repoPath string) string
- func BuildUploadImage(t *testing.T, repoPath string) string
- func CleanupLeaked(ctx context.Context) error
- func ListEmbeddedSnapshots() ([]string, error)
- type Option
- func WithBlockchainImage(image string) Option
- func WithDelegatorImage(image string) Option
- func WithEmbeddedSnapshot(name string) Option
- func WithGuppyImage(image string) Option
- func WithIPNIImage(image string) Option
- func WithIndexerImage(image string) Option
- func WithIngotImage(image string) Option
- func WithKeepOnFailure() Option
- func WithPiriBinary(path string) Option
- func WithPiriCount(n int) Option
- func WithPiriImage(image string) Option
- func WithPiriNodes(nodes ...PiriNodeConfig) Option
- func WithServiceBinary(service, path string) Option
- func WithServiceConfig(service, path string) Option
- func WithSignerImage(image string) Option
- func WithSnapshot(path string) Option
- func WithTimeout(d time.Duration) Option
- func WithUploadImage(image string) Option
- func WithWorkspaceBinaries() Option
- type PiriNodeConfig
- type Stack
- func (s *Stack) Close(ctx context.Context) error
- func (s *Stack) EmailEndpoint() string
- func (s *Stack) Exec(ctx context.Context, service string, args ...string) (stdout, stderr string, err error)
- func (s *Stack) IngotEndpoint() string
- func (s *Stack) Logs(ctx context.Context, service string) (string, error)
- func (s *Stack) PiriCount() int
- func (s *Stack) PiriEndpointN(index int) string
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildDelegatorImage ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
WithBlockchainImage sets the blockchain (Anvil) container image.
func WithDelegatorImage ¶
WithDelegatorImage sets the delegator container image.
func WithEmbeddedSnapshot ¶
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 ¶
WithGuppyImage sets the guppy container image.
func WithIPNIImage ¶
WithIPNIImage sets the IPNI container image.
func WithIndexerImage ¶
WithIndexerImage sets the indexer container image.
func WithIngotImage ¶
WithIngotImage sets the ingot container image.
func WithKeepOnFailure ¶
func WithKeepOnFailure() Option
WithKeepOnFailure prevents cleanup when a test fails, useful for debugging.
func WithPiriBinary ¶
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 ¶
WithPiriCount configures N identical piri nodes with default storage settings.
Example:
s := stack.MustNewStack(t, stack.WithPiriCount(3))
func WithPiriImage ¶
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 ¶
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 ¶
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 ¶
WithSignerImage sets the signing service container image.
func WithSnapshot ¶
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 ¶
WithTimeout sets the maximum time to wait for the stack to start.
func WithUploadImage ¶
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 ¶
MustNewStack creates and starts a network, calling t.Fatal on error.
func NewStack ¶
NewStack creates and starts a complete Forge network. Returns error if startup fails. Cleanup is automatically registered via t.Cleanup().
func (*Stack) Close ¶
Close shuts down the stack and cleans up resources. This is called automatically via t.Cleanup(), but can be called manually.
func (*Stack) EmailEndpoint ¶
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 ¶
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) PiriEndpointN ¶
PiriEndpointN returns the HTTP endpoint for the Nth piri node.