| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package plugin |
| |
| import ( |
| "context" |
| "os" |
| "testing" |
| "time" |
| |
| log "github.com/hashicorp/go-hclog" |
| gplugin "github.com/hashicorp/go-plugin" |
| "github.com/hashicorp/vault/sdk/helper/logging" |
| "github.com/hashicorp/vault/sdk/logical" |
| "github.com/hashicorp/vault/sdk/plugin/mock" |
| ) |
| |
| func TestGRPCBackendPlugin_impl(t *testing.T) { |
| var _ gplugin.Plugin = new(GRPCBackendPlugin) |
| var _ logical.Backend = new(backendGRPCPluginClient) |
| } |
| |
| func TestGRPCBackendPlugin_HandleRequest(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| resp, err := b.HandleRequest(context.Background(), &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "kv/foo", |
| Data: map[string]interface{}{ |
| "value": "bar", |
| }, |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if resp.Data["value"] != "bar" { |
| t.Fatalf("bad: %#v", resp) |
| } |
| } |
| |
| func TestGRPCBackendPlugin_SpecialPaths(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| paths := b.SpecialPaths() |
| if paths == nil { |
| t.Fatal("SpecialPaths() returned nil") |
| } |
| } |
| |
| func TestGRPCBackendPlugin_System(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| sys := b.System() |
| if sys == nil { |
| t.Fatal("System() returned nil") |
| } |
| |
| actual := sys.DefaultLeaseTTL() |
| expected := 300 * time.Second |
| |
| if actual != expected { |
| t.Fatalf("bad: %v, expected %v", actual, expected) |
| } |
| } |
| |
| func TestGRPCBackendPlugin_Logger(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| logger := b.Logger() |
| if logger == nil { |
| t.Fatal("Logger() returned nil") |
| } |
| } |
| |
| func TestGRPCBackendPlugin_HandleExistenceCheck(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| checkFound, exists, err := b.HandleExistenceCheck(context.Background(), &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "kv/foo", |
| Data: map[string]interface{}{"value": "bar"}, |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !checkFound { |
| t.Fatal("existence check not found for path 'kv/foo") |
| } |
| if exists { |
| t.Fatal("existence check should have returned 'false' for 'kv/foo'") |
| } |
| } |
| |
| func TestGRPCBackendPlugin_Cleanup(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| b.Cleanup(context.Background()) |
| } |
| |
| func TestGRPCBackendPlugin_InvalidateKey(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| ctx := context.Background() |
| |
| resp, err := b.HandleRequest(ctx, &logical.Request{ |
| Operation: logical.ReadOperation, |
| Path: "internal", |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if resp.Data["value"] == "" { |
| t.Fatalf("bad: %#v, expected non-empty value", resp) |
| } |
| |
| b.InvalidateKey(ctx, "internal") |
| |
| resp, err = b.HandleRequest(ctx, &logical.Request{ |
| Operation: logical.ReadOperation, |
| Path: "internal", |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if resp.Data["value"] != "" { |
| t.Fatalf("bad: expected empty response data, got %#v", resp) |
| } |
| } |
| |
| func TestGRPCBackendPlugin_Setup(t *testing.T) { |
| _, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| } |
| |
| func TestGRPCBackendPlugin_Initialize(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| err := b.Initialize(context.Background(), &logical.InitializationRequest{}) |
| if err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| func TestGRPCBackendPlugin_Version(t *testing.T) { |
| b, cleanup := testGRPCBackend(t) |
| defer cleanup() |
| |
| versioner, ok := b.(logical.PluginVersioner) |
| if !ok { |
| t.Fatalf("Expected %T to implement logical.PluginVersioner interface", b) |
| } |
| |
| version := versioner.PluginVersion().Version |
| if version != "v0.0.0+mock" { |
| t.Fatalf("Got version %s, expected 'mock'", version) |
| } |
| } |
| |
| func testGRPCBackend(t *testing.T) (logical.Backend, func()) { |
| // Create a mock provider |
| pluginMap := map[string]gplugin.Plugin{ |
| "backend": &GRPCBackendPlugin{ |
| Factory: mock.Factory, |
| Logger: log.New(&log.LoggerOptions{ |
| Level: log.Debug, |
| Output: os.Stderr, |
| JSONFormat: true, |
| }), |
| }, |
| } |
| client, _ := gplugin.TestPluginGRPCConn(t, pluginMap) |
| cleanup := func() { |
| client.Close() |
| } |
| |
| // Request the backend |
| raw, err := client.Dispense(BackendPluginName) |
| if err != nil { |
| t.Fatal(err) |
| } |
| b := raw.(logical.Backend) |
| |
| err = b.Setup(context.Background(), &logical.BackendConfig{ |
| Logger: logging.NewVaultLogger(log.Debug), |
| System: &logical.StaticSystemView{ |
| DefaultLeaseTTLVal: 300 * time.Second, |
| MaxLeaseTTLVal: 1800 * time.Second, |
| }, |
| StorageView: &logical.InmemStorage{}, |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| return b, cleanup |
| } |