check / check (push) Waiting to run
A startup error was cut at its last "): " to drop the annotations fx adds, so the blob_size_limit message, which itself contains "(... size spread): ", lost everything but "need at least N bytes". cleanStartupError now uses dig.RootCause, from the library fx is built on, which removes exactly those annotations and returns the error underneath unchanged. The test starts a real fx app on a config whose blob_size_limit is below the minimum. The old cases fed it plain strings that only looked like fx output; such an error is now rightly left whole, so they are replaced. go.uber.org/dig, already required through fx, becomes a direct requirement in go.mod. Model: opus-5-5
105 lines
3.0 KiB
Go
105 lines
3.0 KiB
Go
package cli //nolint:testpackage // needs access to unexported cleanStartupError
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"go.uber.org/fx"
|
|
"sneak.berlin/go/vaultik/internal/config"
|
|
"sneak.berlin/go/vaultik/internal/pidlock"
|
|
)
|
|
|
|
// TestCleanStartupError starts a real fx app whose config has a
|
|
// blob_size_limit below the minimum. fx puts annotations naming the
|
|
// functions it was building in front of the config error; only those
|
|
// may be removed, although the config error itself contains "): ".
|
|
func TestCleanStartupError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
path := filepath.Join(t.TempDir(), "config.yml")
|
|
|
|
err := os.WriteFile(path, []byte("snapshots:\n"+
|
|
" test:\n"+
|
|
" paths: [/tmp/vaultik-test-source]\n"+
|
|
"storage_url: file:///tmp/vaultik-test-storage\n"+
|
|
"chunk_size: 10MB\n"+
|
|
"blob_size_limit: 30MB\n"), 0o600)
|
|
if err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
|
|
app := fx.New(
|
|
fx.NopLogger,
|
|
fx.Supply(config.Path(path)),
|
|
config.Module,
|
|
fx.Invoke(func(*config.Config) {}),
|
|
)
|
|
|
|
err = app.Start(t.Context())
|
|
if err == nil {
|
|
t.Fatal("app started; want a blob_size_limit error")
|
|
}
|
|
|
|
want := "failed to load config: invalid config: blob_size_limit must " +
|
|
"be at least the largest chunk the chunker can emit (chunk_size " +
|
|
"times the FastCDC size spread): need at least 40000000 bytes"
|
|
|
|
got := cleanStartupError(err).Error()
|
|
if got != want {
|
|
t.Errorf("got %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
// TestCleanStartupErrorNotFromFx checks that an error fx did not wrap is
|
|
// shown unchanged.
|
|
func TestCleanStartupErrorNotFromFx(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
//nolint:err113 // a one-off error standing in for a startup failure
|
|
got := cleanStartupError(errors.New("plain error")).Error()
|
|
if got != "plain error" {
|
|
t.Errorf("got %q, want %q", got, "plain error")
|
|
}
|
|
}
|
|
|
|
// TestLockScopedToMutatingCommands proves the partition the PID lock now
|
|
// enforces: a read-only command runs while a mutator holds the lock, and
|
|
// two mutating commands still mutually exclude.
|
|
func TestLockScopedToMutatingCommands(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
lockDir := filepath.Join(t.TempDir(), "vaultik")
|
|
|
|
// A mutating command takes the process-wide lock.
|
|
releaseMutator, err := acquireLockIfMutating(mutating, lockDir)
|
|
if err != nil {
|
|
t.Fatalf("mutating command could not acquire lock: %v", err)
|
|
}
|
|
|
|
// A read-only command runs to completion even while the lock is held.
|
|
releaseReader, err := acquireLockIfMutating(readOnly, lockDir)
|
|
if err != nil {
|
|
t.Fatalf("read-only command was blocked by held lock: %v", err)
|
|
}
|
|
|
|
releaseReader()
|
|
|
|
// A second mutating command is refused while the first holds the lock.
|
|
_, err = acquireLockIfMutating(mutating, lockDir)
|
|
if !errors.Is(err, pidlock.ErrAlreadyRunning) {
|
|
t.Fatalf("second mutating command was not excluded, got: %v", err)
|
|
}
|
|
|
|
// Once the first mutator releases, another mutating command may run.
|
|
releaseMutator()
|
|
|
|
release, err := acquireLockIfMutating(mutating, lockDir)
|
|
if err != nil {
|
|
t.Fatalf("mutating command could not acquire released lock: %v", err)
|
|
}
|
|
|
|
release()
|
|
}
|