package vaultik_test import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "sneak.berlin/go/vaultik/internal/config" "sneak.berlin/go/vaultik/internal/database" "sneak.berlin/go/vaultik/internal/log" "sneak.berlin/go/vaultik/internal/vaultik" ) // buildBindTestVaultik returns a minimal Vaultik wired with a real // in-memory DB and a config carrying the given StorageURL. Enough to // exercise EnsureStorageBinding without spinning up storage or fx. func buildBindTestVaultik( t *testing.T, storageURL string, ) (*vaultik.TestVaultik, *database.Repositories) { t.Helper() db, err := database.NewTestDB() require.NoError(t, err) t.Cleanup(func() { _ = db.Close() }) repos := database.NewRepositories(db) tv := vaultik.NewForTesting(nil) tv.Repositories = repos tv.DB = db tv.Config = &config.Config{StorageURL: storageURL} tv.SetContext(context.Background()) return tv, repos } func TestEnsureStorageBinding_FreshDBRecordsURL(t *testing.T) { log.Initialize(log.Config{}) t.Parallel() tv, repos := buildBindTestVaultik(t, "file:///mnt/backups/new") require.NoError(t, tv.EnsureStorageBinding()) got, err := repos.LocalMeta.Get(context.Background(), database.LocalMetaKeyStorageURL) require.NoError(t, err) assert.Equal(t, "file:///mnt/backups/new", got, "first call must record the configured URL") } func TestEnsureStorageBinding_MatchingURLPasses(t *testing.T) { log.Initialize(log.Config{}) t.Parallel() tv, repos := buildBindTestVaultik(t, "s3://bucket/prefix") require.NoError(t, repos.LocalMeta.Set(context.Background(), database.LocalMetaKeyStorageURL, "s3://bucket/prefix")) require.NoError(t, tv.EnsureStorageBinding()) } func TestEnsureStorageBinding_MismatchRefuses(t *testing.T) { log.Initialize(log.Config{}) t.Parallel() tv, repos := buildBindTestVaultik(t, "file:///mnt/backups/new") require.NoError(t, repos.LocalMeta.Set(context.Background(), database.LocalMetaKeyStorageURL, "file:///mnt/backups/old")) err := tv.EnsureStorageBinding() require.Error(t, err) assert.Contains(t, err.Error(), "file:///mnt/backups/old") assert.Contains(t, err.Error(), "file:///mnt/backups/new") assert.Contains(t, err.Error(), "vaultik database delete") } // A config with no storage_url names its destination with s3.endpoint, // s3.bucket and s3.prefix; a change to any of them must be refused just // as a changed storage_url is. func TestEnsureStorageBinding_S3FieldsChangeRefuses(t *testing.T) { log.Initialize(log.Config{}) t.Parallel() bound := config.S3Config{ Endpoint: "https://s3.example.com", Bucket: "bucket-a", Prefix: "backups", } tests := []struct { name string changed config.S3Config }{ {"bucket", config.S3Config{ Endpoint: bound.Endpoint, Bucket: "bucket-b", Prefix: bound.Prefix, }}, {"prefix", config.S3Config{ Endpoint: bound.Endpoint, Bucket: bound.Bucket, Prefix: "other", }}, {"endpoint", config.S3Config{ Endpoint: "https://s3.other.com", Bucket: bound.Bucket, Prefix: bound.Prefix, }}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() tv, _ := buildBindTestVaultik(t, "") tv.Config.S3 = bound require.NoError(t, tv.EnsureStorageBinding(), "first use binds") require.NoError(t, tv.EnsureStorageBinding(), "same destination passes") tv.Config.S3 = tt.changed err := tv.EnsureStorageBinding() require.Error(t, err) assert.Contains(t, err.Error(), "local index is bound to a different backup destination") assert.Contains(t, err.Error(), "vaultik database delete") }) } }