From 5887e598e50c7ffdf3bffc9e44fc026ea12a0861 Mon Sep 17 00:00:00 2001 From: rupinder-syngh Date: Thu, 3 Apr 2025 15:31:09 +0530 Subject: [PATCH] feat: Implement Manager.go test --- pkg/plugin/manager_test.go | 1764 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1764 insertions(+) create mode 100644 pkg/plugin/manager_test.go diff --git a/pkg/plugin/manager_test.go b/pkg/plugin/manager_test.go new file mode 100644 index 0000000..b5f2104 --- /dev/null +++ b/pkg/plugin/manager_test.go @@ -0,0 +1,1764 @@ +package plugin + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "plugin" + "strings" + "testing" + "time" + + "archive/zip" + + "github.com/beckn/beckn-onix/pkg/model" + "github.com/beckn/beckn-onix/pkg/plugin/definition" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Mock implementations of plugin interfaces +type MockSigner struct{} + +func (m *MockSigner) Sign(ctx context.Context, body []byte, privateKeyBase64 string, createdAt, expiresAt int64) (string, error) { + return "mock_signature", nil +} + +func (m *MockSigner) Close() error { + return nil +} + +type MockSignValidator struct{} + +func (m *MockSignValidator) Validate(ctx context.Context, body []byte, header string, publicKeyBase64 string) error { + return nil +} + +func (m *MockSignValidator) Close() error { + return nil +} + +type MockDecrypter struct{} + +func (m *MockDecrypter) Decrypt(ctx context.Context, data, key, iv string) (string, error) { + return "decrypted_data", nil +} + +func (m *MockDecrypter) Close() error { + return nil +} + +type MockEncrypter struct{} + +func (m *MockEncrypter) Encrypt(ctx context.Context, data, key, iv string) (string, error) { + return "encrypted_data", nil +} + +func (m *MockEncrypter) Close() error { + return nil +} + +type MockPublisher struct{} + +func (m *MockPublisher) Publish(ctx context.Context, topic string, message []byte) error { + return nil +} + +func (m *MockPublisher) Close() error { + return nil +} + +// Mock providers for each plugin type +type MockSignerProvider struct { + newFunc func(ctx context.Context, config map[string]string) (definition.Signer, func() error, error) +} + +func (m *MockSignerProvider) New(ctx context.Context, config map[string]string) (definition.Signer, func() error, error) { + if config == nil { + return nil, nil, errors.New("failed to load provider") + } + return m.newFunc(ctx, config) +} + +type MockSignValidatorProvider struct { + newFunc func(ctx context.Context, config map[string]string) (definition.SignValidator, func() error, error) +} + +func (m *MockSignValidatorProvider) New(ctx context.Context, config map[string]string) (definition.SignValidator, func() error, error) { + return m.newFunc(ctx, config) +} + +type MockDecrypterProvider struct { + newFunc func(ctx context.Context, config map[string]string) (definition.Decrypter, func() error, error) +} + +func (m *MockDecrypterProvider) New(ctx context.Context, config map[string]string) (definition.Decrypter, func() error, error) { + return m.newFunc(ctx, config) +} + +type MockEncrypterProvider struct { + newFunc func(ctx context.Context, config map[string]string) (definition.Encrypter, func() error, error) +} + +func (m *MockEncrypterProvider) New(ctx context.Context, config map[string]string) (definition.Encrypter, func() error, error) { + return m.newFunc(ctx, config) +} + +type MockPublisherProvider struct { + newFunc func(ctx context.Context, config map[string]string) (definition.Publisher, error) +} + +func (m *MockPublisherProvider) New(ctx context.Context, config map[string]string) (definition.Publisher, error) { + return m.newFunc(ctx, config) +} + +// MockStepProvider is a mock implementation of StepProvider +type MockStepProvider struct { + newFunc func(ctx context.Context, config map[string]string) (definition.Step, func(), error) +} + +func (m *MockStepProvider) New(ctx context.Context, config map[string]string) (definition.Step, func(), error) { + if m.newFunc != nil { + return m.newFunc(ctx, config) + } + return nil, nil, errors.New("new not implemented") +} + +// TestNewManager tests the NewManager function +func TestNewManager_Success(t *testing.T) { + // Create a temporary directory for testing + tmpDir, err := os.MkdirTemp("", "plugin-test-*") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + + // Create a valid plugin directory + validPluginDir := filepath.Join(tmpDir, "plugins") + err = os.MkdirAll(validPluginDir, 0755) + require.NoError(t, err) + + tests := []struct { + name string + cfg *ManagerConfig + wantErr bool + errContains string + }{ + { + name: "valid configuration", + cfg: &ManagerConfig{ + Root: validPluginDir, + RemoteRoot: "", + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + manager, closer, err := NewManager(context.Background(), tt.cfg) + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, manager) + assert.Nil(t, closer) + } else { + assert.NoError(t, err) + assert.NotNil(t, manager) + assert.NotNil(t, closer) + if closer != nil { + closer() + } + } + }) + } +} + +// TestNewManager_Failure tests various failure scenarios for NewManager +func TestNewManager_Failure(t *testing.T) { + // Create a temporary directory for testing + tmpDir, err := os.MkdirTemp("", "plugin-test-*") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + tests := []struct { + name string + cfg *ManagerConfig + wantErr bool + errContains string + }{ + { + name: "non-existent root path", + cfg: &ManagerConfig{ + Root: filepath.Join(tmpDir, "nonexistent"), + }, + wantErr: true, + errContains: "no such file or directory", + }, + { + name: "non-existent remote root", + cfg: &ManagerConfig{ + Root: tmpDir, + RemoteRoot: filepath.Join(tmpDir, "nonexistent.zip"), + }, + wantErr: true, + errContains: "no such file or directory", + }, + { + name: "invalid remote root zip", + cfg: &ManagerConfig{ + Root: tmpDir, + RemoteRoot: filepath.Join(tmpDir, "invalid.zip"), + }, + wantErr: true, + errContains: "zip: not a valid zip file", + }, + } + + // Create an invalid zip file for testing + invalidZipPath := filepath.Join(tmpDir, "invalid.zip") + if err := os.WriteFile(invalidZipPath, []byte("not a zip file"), 0644); err != nil { + t.Fatalf("Failed to create invalid zip file: %v", err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + manager, cleanup, err := NewManager(context.Background(), tt.cfg) + if tt.wantErr { + if err == nil { + t.Error("Expected error, got nil") + } else if !contains(err.Error(), tt.errContains) { + t.Errorf("Expected error containing %q, got %q", tt.errContains, err.Error()) + } + if manager != nil { + t.Error("Expected nil manager, got non-nil") + } + if cleanup != nil { + t.Error("Expected nil cleanup function, got non-nil") + } + } else { + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if manager == nil { + t.Error("Expected non-nil manager, got nil") + } + if cleanup == nil { + t.Error("Expected non-nil cleanup function, got nil") + } + if cleanup != nil { + cleanup() + } + } + }) + } +} + +// Helper function to check if a string contains a substring +func contains(s, substr string) bool { + return strings.Contains(s, substr) +} + +// TestProvider tests the provider function +func TestProvider(t *testing.T) { + type testProvider struct { + value string + } + + // Create a real plugin that exports a Provider symbol + pluginPath := filepath.Join(os.TempDir(), "test_provider.so") + defer os.Remove(pluginPath) + + // Create a simple Go file that exports a Provider symbol + goFile := filepath.Join(os.TempDir(), "test_provider.go") + err := os.WriteFile(goFile, []byte(` +package main + +type TestProvider struct { + value string +} + +var Provider = &TestProvider{value: "test"} + +func main() {} +`), 0644) + if err != nil { + t.Fatalf("Failed to create test plugin source: %v", err) + } + defer os.Remove(goFile) + + // Build the plugin + cmd := exec.Command("go", "build", "-buildmode=plugin", "-o", pluginPath, goFile) + if err := cmd.Run(); err != nil { + t.Fatalf("Failed to build test plugin: %v", err) + } + + // Load the plugin + p, err := plugin.Open(pluginPath) + if err != nil { + t.Fatalf("Failed to load test plugin: %v", err) + } + + tests := []struct { + name string + plugins map[string]*plugin.Plugin + id string + wantErr bool + errContains string + verify func(t *testing.T, got interface{}) + }{ + { + name: "success - valid provider", + plugins: map[string]*plugin.Plugin{ + "test-provider": p, + }, + id: "test-provider", + wantErr: false, + verify: func(t *testing.T, got interface{}) { + provider, ok := got.(*testProvider) + assert.True(t, ok) + assert.Equal(t, "test", provider.value) + }, + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + id: "nonexistent", + wantErr: true, + errContains: "plugin nonexistent not found", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + id: "test-provider", + wantErr: true, + errContains: "failed to lookup Provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + id: "test-provider", + wantErr: true, + errContains: "failed to lookup Provider for test-provider", + }, + { + name: "error - empty plugin map", + plugins: map[string]*plugin.Plugin{}, + id: "test-provider", + wantErr: true, + errContains: "plugin test-provider not found", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := provider[testProvider](tt.plugins, tt.id) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + if tt.verify != nil { + tt.verify(t, got) + } + }) + } +} + +func TestUnzip(t *testing.T) { + // Create a temporary directory for testing + tempDir, err := os.MkdirTemp("", "unzip-test-*") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + // Create test cases + tests := []struct { + name string + setup func() (string, string, error) + wantErr bool + validateFiles func(string) error + }{ + { + name: "successful unzip with single file", + setup: func() (string, string, error) { + // Create a temporary zip file + zipPath := filepath.Join(tempDir, "test.zip") + zipFile, err := os.Create(zipPath) + if err != nil { + return "", "", err + } + defer zipFile.Close() + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + defer zipWriter.Close() + + // Add a file to the zip + fileInZip, err := zipWriter.Create("test.txt") + if err != nil { + return "", "", err + } + fileInZip.Write([]byte("test content")) + + return zipPath, filepath.Join(tempDir, "output"), nil + }, + wantErr: false, + validateFiles: func(dest string) error { + // Check if the file was extracted + extractedFile := filepath.Join(dest, "test.txt") + if _, err := os.Stat(extractedFile); err != nil { + return err + } + return nil + }, + }, + { + name: "successful unzip with nested directories", + setup: func() (string, string, error) { + // Create a temporary zip file + zipPath := filepath.Join(tempDir, "nested.zip") + zipFile, err := os.Create(zipPath) + if err != nil { + return "", "", err + } + defer zipFile.Close() + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + defer zipWriter.Close() + + // Add a file in a nested directory + fileInZip, err := zipWriter.Create("nested/test.txt") + if err != nil { + return "", "", err + } + fileInZip.Write([]byte("nested content")) + + return zipPath, filepath.Join(tempDir, "nested-output"), nil + }, + wantErr: false, + validateFiles: func(dest string) error { + // Check if the nested file was extracted + extractedFile := filepath.Join(dest, "nested", "test.txt") + if _, err := os.Stat(extractedFile); err != nil { + return err + } + return nil + }, + }, + { + name: "non-existent zip file", + setup: func() (string, string, error) { + return "non-existent.zip", filepath.Join(tempDir, "output"), nil + }, + wantErr: true, + validateFiles: func(dest string) error { + return nil + }, + }, + { + name: "invalid zip file", + setup: func() (string, string, error) { + // Create an invalid zip file + zipPath := filepath.Join(tempDir, "invalid.zip") + if err := os.WriteFile(zipPath, []byte("not a zip file"), 0644); err != nil { + return "", "", err + } + return zipPath, filepath.Join(tempDir, "output"), nil + }, + wantErr: true, + validateFiles: func(dest string) error { + return nil + }, + }, + { + name: "destination directory creation failure", + setup: func() (string, string, error) { + // Create a temporary zip file + zipPath := filepath.Join(tempDir, "test.zip") + zipFile, err := os.Create(zipPath) + if err != nil { + return "", "", err + } + defer zipFile.Close() + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + defer zipWriter.Close() + + // Add a file to the zip + fileInZip, err := zipWriter.Create("test.txt") + if err != nil { + return "", "", err + } + fileInZip.Write([]byte("test content")) + + // Create a read-only directory + readOnlyDir := filepath.Join(tempDir, "readonly") + if err := os.MkdirAll(readOnlyDir, 0444); err != nil { + return "", "", err + } + + return zipPath, readOnlyDir, nil + }, + wantErr: true, + validateFiles: func(dest string) error { + return nil + }, + }, + { + name: "file copy failure", + setup: func() (string, string, error) { + // Create a temporary zip file + zipPath := filepath.Join(tempDir, "test.zip") + zipFile, err := os.Create(zipPath) + if err != nil { + return "", "", err + } + defer zipFile.Close() + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + defer zipWriter.Close() + + // Add a file to the zip + fileInZip, err := zipWriter.Create("test.txt") + if err != nil { + return "", "", err + } + fileInZip.Write([]byte("test content")) + + // Create a destination directory with a file that can't be overwritten + destDir := filepath.Join(tempDir, "dest") + if err := os.MkdirAll(destDir, 0755); err != nil { + return "", "", err + } + // Create a read-only file in the destination + readOnlyFile := filepath.Join(destDir, "test.txt") + if err := os.WriteFile(readOnlyFile, []byte("readonly"), 0444); err != nil { + return "", "", err + } + + return zipPath, destDir, nil + }, + wantErr: true, + validateFiles: func(dest string) error { + return nil + }, + }, + { + name: "file copy error due to read-only destination", + setup: func() (string, string, error) { + // Create a temporary zip file + zipPath := filepath.Join(tempDir, "test.zip") + zipFile, err := os.Create(zipPath) + if err != nil { + return "", "", err + } + defer zipFile.Close() + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + defer zipWriter.Close() + + // Add a file to the zip + fileInZip, err := zipWriter.Create("test.txt") + if err != nil { + return "", "", err + } + fileInZip.Write([]byte("test content")) + + // Create a read-only destination directory + destDir := filepath.Join(tempDir, "readonly-dest") + if err := os.MkdirAll(destDir, 0444); err != nil { + return "", "", err + } + + return zipPath, destDir, nil + }, + wantErr: true, + validateFiles: func(dest string) error { + return nil + }, + }, + { + name: "file copy error due to invalid source file", + setup: func() (string, string, error) { + // Create a temporary zip file + zipPath := filepath.Join(tempDir, "test.zip") + zipFile, err := os.Create(zipPath) + if err != nil { + return "", "", err + } + defer zipFile.Close() + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + defer zipWriter.Close() + + // Add a file to the zip with invalid content + fileInZip, err := zipWriter.Create("test.txt") + if err != nil { + return "", "", err + } + // Write some content that will cause an error during extraction + fileInZip.Write([]byte("test content")) + + // Create a destination directory + destDir := filepath.Join(tempDir, "dest") + if err := os.MkdirAll(destDir, 0755); err != nil { + return "", "", err + } + + // Close the zip file to ensure it's written + if err := zipWriter.Close(); err != nil { + return "", "", err + } + if err := zipFile.Close(); err != nil { + return "", "", err + } + + // Corrupt the zip file + if err := os.WriteFile(zipPath, []byte("corrupted content"), 0644); err != nil { + return "", "", err + } + + return zipPath, destDir, nil + }, + wantErr: true, + validateFiles: func(dest string) error { + return nil + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + src, dest, err := tt.setup() + if err != nil { + t.Fatalf("Setup failed: %v", err) + } + + err = unzip(src, dest) + if (err != nil) != tt.wantErr { + t.Errorf("unzip() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !tt.wantErr { + if err := tt.validateFiles(dest); err != nil { + t.Errorf("File validation failed: %v", err) + } + } + }) + } +} + +func TestValidator(t *testing.T) { + tests := []struct { + name string + cfg *Config + plugins map[string]*plugin.Plugin + wantPanic bool + panicMsg string + }{ + { + name: "unimplemented panic", + cfg: &Config{ + ID: "test-validator", + Config: map[string]string{}, + }, + plugins: make(map[string]*plugin.Plugin), + wantPanic: true, + panicMsg: "unimplemented", + }, + { + name: "nil config", + cfg: nil, + plugins: map[string]*plugin.Plugin{ + "test-validator": &plugin.Plugin{}, + }, + wantPanic: true, + panicMsg: "unimplemented", + }, + { + name: "empty config panic", + cfg: &Config{ + ID: "", + Config: nil, + }, + plugins: map[string]*plugin.Plugin{ + "test-validator": &plugin.Plugin{}, + }, + wantPanic: true, + panicMsg: "unimplemented", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + if tt.wantPanic { + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic, got none") + } else if !contains(fmt.Sprint(r), tt.panicMsg) { + t.Errorf("Expected panic containing %q, got %q", tt.panicMsg, r) + } + }() + } + + got, err := m.Validator(context.Background(), tt.cfg) + if !tt.wantPanic { + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if got == nil { + t.Error("Expected non-nil validator, got nil") + } + } + }) + } +} + +// MockKeyManager is a mock implementation of KeyManager +type MockKeyManager struct{} + +func (m *MockKeyManager) GetPublicKey(ctx context.Context, subscriberId string) (string, error) { + return "mock_public_key", nil +} + +func (m *MockKeyManager) DeletePrivateKeys(ctx context.Context, subscriberId string) error { + return nil +} + +func (m *MockKeyManager) Close() error { + return nil +} + +func (m *MockKeyManager) GenerateKeyPairs() (*model.Keyset, error) { + return &model.Keyset{ + UniqueKeyID: "mock_key_id", + SigningPrivate: "mock_signing_private", + SigningPublic: "mock_signing_public", + EncrPrivate: "mock_encr_private", + EncrPublic: "mock_encr_public", + }, nil +} + +func (m *MockKeyManager) StorePrivateKeys(ctx context.Context, keyID string, keys *model.Keyset) error { + return nil +} + +func (m *MockKeyManager) SigningPrivateKey(ctx context.Context, keyID string) (string, string, error) { + return "mock_signing_private", "mock_iv", nil +} + +func (m *MockKeyManager) EncrPrivateKey(ctx context.Context, keyID string) (string, string, error) { + return "mock_encr_private", "mock_iv", nil +} + +func (m *MockKeyManager) SigningPublicKey(ctx context.Context, subscriberID, uniqueKeyID string) (string, error) { + return "mock_signing_public", nil +} + +func (m *MockKeyManager) EncrPublicKey(ctx context.Context, subscriberID, uniqueKeyID string) (string, error) { + return "mock_encr_public", nil +} + +// MockKeyManagerProvider is a mock implementation of KeyManagerProvider +type MockKeyManagerProvider struct { + newFunc func(ctx context.Context, cache definition.Cache, rClient definition.RegistryLookup, config map[string]string) (definition.KeyManager, func() error, error) +} + +func (m *MockKeyManagerProvider) New(ctx context.Context, cache definition.Cache, rClient definition.RegistryLookup, config map[string]string) (definition.KeyManager, func() error, error) { + if config == nil { + return nil, nil, errors.New("config cannot be nil") + } + return m.newFunc(ctx, cache, rClient, config) +} + +// MockCache is a mock implementation of Cache +type MockCache struct{} + +func (m *MockCache) Get(ctx context.Context, key string) (string, error) { + return "", nil +} + +func (m *MockCache) Set(ctx context.Context, key string, value string, ttl time.Duration) error { + return nil +} + +func (m *MockCache) Delete(ctx context.Context, key string) error { + return nil +} + +func (m *MockCache) Clear(ctx context.Context) error { + return nil +} + +func (m *MockCache) Close() error { + return nil +} + +// MockRegistryLookup is a mock implementation of RegistryLookup +type MockRegistryLookup struct{} + +func (m *MockRegistryLookup) LookupRegistry(ctx context.Context, subscriberId string) (string, error) { + return "mock_registry", nil +} + +func (m *MockRegistryLookup) Lookup(ctx context.Context, subscription *model.Subscription) ([]model.Subscription, error) { + return []model.Subscription{}, nil +} + +// MockPlugin is a mock implementation of plugin.Plugin +type MockPlugin struct { + lookupFunc func(symName string) (plugin.Symbol, error) +} + +func (m *MockPlugin) Lookup(symName string) (plugin.Symbol, error) { + return m.lookupFunc(symName) +} + +func TestKeyManager(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + cache definition.Cache + rClient definition.RegistryLookup + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + cache: &MockCache{}, + rClient: &MockRegistryLookup{}, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + cache: &MockCache{}, + rClient: &MockRegistryLookup{}, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + cache: &MockCache{}, + rClient: &MockRegistryLookup{}, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + cache: &MockCache{}, + rClient: &MockRegistryLookup{}, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + cache: &MockCache{}, + rClient: &MockRegistryLookup{}, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.KeyManager(context.Background(), tt.cache, tt.rClient, tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestPublisher(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.Publisher(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestSchemaValidator(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.SchemaValidator(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestRouter(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.Router(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestMiddleware(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.Middleware(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestStep(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.Step(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestCache(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.Cache(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestSigner(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.Signer(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestEncryptor(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.Encryptor(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestDecryptor(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.Decryptor(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +} + +func TestSignValidator(t *testing.T) { + tests := []struct { + name string + plugins map[string]*plugin.Plugin + cfg *Config + wantErr bool + errContains string + }{ + { + name: "error - nil config", + plugins: make(map[string]*plugin.Plugin), + cfg: nil, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - empty config", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "", + Config: nil, + }, + wantErr: true, + errContains: "failed to load provider for", + }, + { + name: "error - plugin not found", + plugins: make(map[string]*plugin.Plugin), + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - provider lookup failed", + plugins: map[string]*plugin.Plugin{ + "test-provider": &plugin.Plugin{}, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + { + name: "error - nil plugin", + plugins: map[string]*plugin.Plugin{ + "test-provider": nil, + }, + cfg: &Config{ + ID: "test-provider", + Config: map[string]string{}, + }, + wantErr: true, + errContains: "failed to load provider for test-provider", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Manager{ + plugins: tt.plugins, + closers: make([]func(), 0), + } + + got, err := m.SignValidator(context.Background(), tt.cfg) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotNil(t, got) + }) + } +}