github.com/cornelk/go-cloud@v0.17.1/secrets/secrets_test.go (about)

     1  // Copyright 2019 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package secrets
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"net/url"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  
    25  	"github.com/cornelk/go-cloud/gcerrors"
    26  	"github.com/cornelk/go-cloud/internal/gcerr"
    27  	"github.com/cornelk/go-cloud/internal/testing/octest"
    28  	"github.com/cornelk/go-cloud/secrets/driver"
    29  	"github.com/google/go-cmp/cmp"
    30  )
    31  
    32  var errFake = errors.New("fake")
    33  
    34  type erroringKeeper struct {
    35  	driver.Keeper
    36  }
    37  
    38  func (k *erroringKeeper) Decrypt(ctx context.Context, b []byte) ([]byte, error) {
    39  	return nil, errFake
    40  }
    41  
    42  func (k *erroringKeeper) Encrypt(ctx context.Context, b []byte) ([]byte, error) {
    43  	return nil, errFake
    44  }
    45  
    46  func (k *erroringKeeper) Close() error                       { return errFake }
    47  func (k *erroringKeeper) ErrorCode(error) gcerrors.ErrorCode { return gcerrors.Internal }
    48  
    49  func TestErrorsAreWrapped(t *testing.T) {
    50  	ctx := context.Background()
    51  	k := NewKeeper(&erroringKeeper{})
    52  
    53  	// verifyWrap ensures that err is wrapped exactly once.
    54  	verifyWrap := func(description string, err error) {
    55  		if err == nil {
    56  			t.Errorf("%s: got nil error, wanted non-nil", description)
    57  		} else if unwrapped, ok := err.(*gcerr.Error); !ok {
    58  			t.Errorf("%s: not wrapped: %v", description, err)
    59  		} else if du, ok := unwrapped.Unwrap().(*gcerr.Error); ok {
    60  			t.Errorf("%s: double wrapped: %v", description, du)
    61  		}
    62  		if s := err.Error(); !strings.HasPrefix(s, "secrets ") {
    63  			t.Errorf("%s: Error() for wrapped error doesn't start with secrets: prefix: %s", description, s)
    64  		}
    65  	}
    66  
    67  	_, err := k.Decrypt(ctx, nil)
    68  	verifyWrap("Decrypt", err)
    69  
    70  	_, err = k.Encrypt(ctx, nil)
    71  	verifyWrap("Encrypt", err)
    72  
    73  	err = k.Close()
    74  	verifyWrap("Close", err)
    75  }
    76  
    77  // TestKeeperIsClosed tests that Keeper functions return an error when the
    78  // Keeper is closed.
    79  func TestKeeperIsClosed(t *testing.T) {
    80  	ctx := context.Background()
    81  	k := NewKeeper(&erroringKeeper{})
    82  	k.Close()
    83  
    84  	if _, err := k.Decrypt(ctx, nil); err != errClosed {
    85  		t.Error(err)
    86  	}
    87  	if _, err := k.Encrypt(ctx, nil); err != errClosed {
    88  		t.Error(err)
    89  	}
    90  	if err := k.Close(); err != errClosed {
    91  		t.Error(err)
    92  	}
    93  }
    94  
    95  func TestOpenCensus(t *testing.T) {
    96  	ctx := context.Background()
    97  	te := octest.NewTestExporter(OpenCensusViews)
    98  	defer te.Unregister()
    99  
   100  	k := NewKeeper(&erroringKeeper{})
   101  	defer k.Close()
   102  	k.Encrypt(ctx, nil)
   103  	k.Decrypt(ctx, nil)
   104  	diff := octest.Diff(te.Spans(), te.Counts(), "github.com/cornelk/go-cloud/secrets", "github.com/cornelk/go-cloud/secrets", []octest.Call{
   105  		{Method: "Encrypt", Code: gcerrors.Internal},
   106  		{Method: "Decrypt", Code: gcerrors.Internal},
   107  	})
   108  	if diff != "" {
   109  		t.Error(diff)
   110  	}
   111  }
   112  
   113  var (
   114  	testOpenOnce sync.Once
   115  	testOpenGot  *url.URL
   116  )
   117  
   118  func TestURLMux(t *testing.T) {
   119  	ctx := context.Background()
   120  
   121  	mux := new(URLMux)
   122  	fake := &fakeOpener{}
   123  	mux.RegisterKeeper("foo", fake)
   124  	mux.RegisterKeeper("err", fake)
   125  
   126  	if diff := cmp.Diff(mux.KeeperSchemes(), []string{"err", "foo"}); diff != "" {
   127  		t.Errorf("Schemes: %s", diff)
   128  	}
   129  	if !mux.ValidKeeperScheme("foo") || !mux.ValidKeeperScheme("err") {
   130  		t.Errorf("ValidKeeperScheme didn't return true for valid scheme")
   131  	}
   132  	if mux.ValidKeeperScheme("foo2") || mux.ValidKeeperScheme("http") {
   133  		t.Errorf("ValidKeeperScheme didn't return false for invalid scheme")
   134  	}
   135  
   136  	for _, tc := range []struct {
   137  		name    string
   138  		url     string
   139  		wantErr bool
   140  	}{
   141  		{
   142  			name:    "empty URL",
   143  			wantErr: true,
   144  		},
   145  		{
   146  			name:    "invalid URL",
   147  			url:     ":foo",
   148  			wantErr: true,
   149  		},
   150  		{
   151  			name:    "invalid URL no scheme",
   152  			url:     "foo",
   153  			wantErr: true,
   154  		},
   155  		{
   156  			name:    "unregistered scheme",
   157  			url:     "bar://mykeeper",
   158  			wantErr: true,
   159  		},
   160  		{
   161  			name:    "func returns error",
   162  			url:     "err://mykeeper",
   163  			wantErr: true,
   164  		},
   165  		{
   166  			name: "no query options",
   167  			url:  "foo://mykeeper",
   168  		},
   169  		{
   170  			name: "empty query options",
   171  			url:  "foo://mykeeper?",
   172  		},
   173  		{
   174  			name: "query options",
   175  			url:  "foo://mykeeper?aAa=bBb&cCc=dDd",
   176  		},
   177  		{
   178  			name: "multiple query options",
   179  			url:  "foo://mykeeper?x=a&x=b&x=c",
   180  		},
   181  		{
   182  			name: "fancy keeper name",
   183  			url:  "foo:///foo/bar/baz",
   184  		},
   185  		{
   186  			name: "using api scheme prefix",
   187  			url:  "secrets+foo://mykeeper",
   188  		},
   189  		{
   190  			name: "using api+type scheme prefix",
   191  			url:  "secrets+keeper+foo://mykeeper",
   192  		},
   193  	} {
   194  		t.Run(tc.name, func(t *testing.T) {
   195  			keeper, gotErr := mux.OpenKeeper(ctx, tc.url)
   196  			if (gotErr != nil) != tc.wantErr {
   197  				t.Fatalf("got err %v, want error %v", gotErr, tc.wantErr)
   198  			}
   199  			if gotErr != nil {
   200  				return
   201  			}
   202  			defer keeper.Close()
   203  			if got := fake.u.String(); got != tc.url {
   204  				t.Errorf("got %q want %q", got, tc.url)
   205  			}
   206  			// Repeat with OpenKeeperURL.
   207  			parsed, err := url.Parse(tc.url)
   208  			if err != nil {
   209  				t.Fatal(err)
   210  			}
   211  			keeper, gotErr = mux.OpenKeeperURL(ctx, parsed)
   212  			if gotErr != nil {
   213  				t.Fatalf("got err %v, want nil", gotErr)
   214  			}
   215  			defer keeper.Close()
   216  			if got := fake.u.String(); got != tc.url {
   217  				t.Errorf("got %q want %q", got, tc.url)
   218  			}
   219  		})
   220  	}
   221  }
   222  
   223  type fakeOpener struct {
   224  	u *url.URL // last url passed to OpenKeeperURL
   225  }
   226  
   227  func (o *fakeOpener) OpenKeeperURL(ctx context.Context, u *url.URL) (*Keeper, error) {
   228  	if u.Scheme == "err" {
   229  		return nil, errors.New("fail")
   230  	}
   231  	o.u = u
   232  	return NewKeeper(&erroringKeeper{}), nil
   233  }