package secrets import ( "context" "os" "path/filepath" "testing" "time" ) // TestSOPSBackendList tests listing SOPS secrets from a mock directory. func TestSOPSBackendList(t *testing.T) { dir := t.TempDir() // Create a mock SOPS-encrypted YAML (plaintext for testing since sops may not be installed) os.WriteFile(filepath.Join(dir, "test.yaml"), []byte("key1: value1\nkey2: value2\n"), 0644) backend := NewSOPSBackend(dir) // Without sops binary, load will fail for encrypted files. // Test that the backend handles missing sops gracefully. // For actual tests with sops installed, see integration tests. _ = backend t.Log("SOPS backend created (requires sops binary for integration tests)") } // TestManagerFallback verifies fallback from primary to secondary backend. func TestManagerFallback(t *testing.T) { ctx := context.Background() primary := &mockBackend{name: "primary", data: map[string]string{}, getErr: ErrBackendUnavailable} fallback := &mockBackend{name: "fallback", data: map[string]string{"test": "fallback-value"}} mgr := NewManager(primary, fallback) val, err := mgr.Get(ctx, "test") if err != nil { t.Fatalf("expected fallback to provide value, got: %v", err) } if val != "fallback-value" { t.Errorf("val = %q, want fallback-value", val) } } // TestManagerCache tests in-memory secret caching. func TestManagerCache(t *testing.T) { ctx := context.Background() primary := &mockBackend{name: "primary", data: map[string]string{"cached": "v1"}} mgr := NewManager(primary, nil) mgr.cacheTTL = 60 * time.Second // don't expire during test // First call: hits backend val, err := mgr.Get(ctx, "cached") if err != nil { t.Fatalf("first get: %v", err) } if val != "v1" { t.Errorf("first get = %q, want v1", val) } // Change the value in backend primary.data["cached"] = "v2" // Second call: should hit cache, return old value val, _ = mgr.Get(ctx, "cached") if val != "v1" { t.Errorf("cached get = %q, want v1 (cached)", val) } // Invalidate cache, should get new value mgr.InvalidateCache() val, _ = mgr.Get(ctx, "cached") if val != "v2" { t.Errorf("after invalidate = %q, want v2", val) } } // TestManagerPrimaryName returns the backend identifier. func TestManagerPrimaryName(t *testing.T) { mgr := NewManager(&mockBackend{name: "infisical"}, nil) if mgr.PrimaryName() != "infisical" { t.Errorf("name = %q, want infisical", mgr.PrimaryName()) } } // mockBackend implements Backend for testing. type mockBackend struct { name string data map[string]string getErr error } func (m *mockBackend) Name() string { return m.name } func (m *mockBackend) Get(ctx context.Context, key string) (string, error) { if m.getErr != nil { return "", m.getErr } v, ok := m.data[key] if !ok { return "", ErrNotFound } return v, nil } func (m *mockBackend) List(ctx context.Context) ([]string, error) { keys := make([]string, 0, len(m.data)) for k := range m.data { keys = append(keys, k) } return keys, nil } func (m *mockBackend) Set(ctx context.Context, key string, value string) error { m.data[key] = value return nil }