github.com/thiagoyeds/go-cloud@v0.26.0/runtimevar/runtimevar_test.go (about)

     1  // Copyright 2018 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 runtimevar contains tests that exercises the runtimevar APIs. It does not test
    16  // driver implementations.
    17  package runtimevar
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/gob"
    23  	"encoding/json"
    24  	"errors"
    25  	"fmt"
    26  	"net/url"
    27  	"reflect"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/google/go-cmp/cmp"
    33  	"gocloud.dev/gcerrors"
    34  	"gocloud.dev/internal/gcerr"
    35  	"gocloud.dev/runtimevar/driver"
    36  	"gocloud.dev/secrets/localsecrets"
    37  )
    38  
    39  // How long we wait on a call that is expected to block forever before cancelling it.
    40  const blockingCheckDelay = 25 * time.Millisecond
    41  
    42  // state implements driver.State.
    43  type state struct {
    44  	val        string
    45  	updateTime time.Time
    46  	err        error
    47  }
    48  
    49  func (s *state) Value() (interface{}, error) { return s.val, s.err }
    50  func (s *state) UpdateTime() time.Time       { return s.updateTime }
    51  func (s *state) As(i interface{}) bool       { return false }
    52  
    53  // fakeWatcher is a fake implementation of driver.Watcher that returns a set *state.
    54  type fakeWatcher struct {
    55  	driver.Watcher
    56  
    57  	mu     sync.Mutex
    58  	state  *state
    59  	newval bool // true iff WatchVariable should return state
    60  }
    61  
    62  func (w *fakeWatcher) Set(s *state) {
    63  	w.mu.Lock()
    64  	defer w.mu.Unlock()
    65  	w.state = s
    66  	w.newval = true
    67  }
    68  
    69  func (w *fakeWatcher) WatchVariable(ctx context.Context, prev driver.State) (driver.State, time.Duration) {
    70  	if err := ctx.Err(); err != nil {
    71  		w.Set(&state{err: err})
    72  	}
    73  	w.mu.Lock()
    74  	defer w.mu.Unlock()
    75  	if !w.newval {
    76  		return nil, 1 * time.Millisecond // to avoid spinning
    77  	}
    78  	w.newval = false
    79  	return w.state, 0
    80  }
    81  
    82  func (*fakeWatcher) Close() error                       { return nil }
    83  func (*fakeWatcher) ErrorCode(error) gcerrors.ErrorCode { return gcerrors.Internal }
    84  
    85  func TestVariable_Watch(t *testing.T) {
    86  	fake := &fakeWatcher{}
    87  	v := New(fake)
    88  
    89  	ctx := context.Background()
    90  
    91  	// Watch should block when there's no value yet.
    92  	ctx2, cancel := context.WithTimeout(ctx, blockingCheckDelay)
    93  	defer cancel()
    94  	if _, err := v.Watch(ctx2); err == nil {
    95  		t.Errorf("Watch with no value yet should block: got nil err, want err")
    96  	}
    97  	if ctx2.Err() == nil {
    98  		t.Error("Watch with no value yet should block")
    99  	}
   100  
   101  	// Setting an error value makes Watch return an error.
   102  	fake.Set(&state{err: errFake})
   103  	if _, err := v.Watch(ctx); err == nil {
   104  		t.Fatal("Watch returned non-nil error, want error")
   105  	}
   106  	// But calling Watch again blocks.
   107  	ctx2, cancel = context.WithTimeout(ctx, blockingCheckDelay)
   108  	defer cancel()
   109  	if _, err := v.Watch(ctx2); err == nil {
   110  		t.Errorf("Watch called again with error value should block: got nil err, want err")
   111  	}
   112  	if ctx2.Err() == nil {
   113  		t.Error("Watch called again with error value should block")
   114  	}
   115  
   116  	// Setting a different error makes Watch return again.
   117  	fake.Set(&state{err: errors.New("another fake error")})
   118  	if _, err := v.Watch(ctx); err == nil {
   119  		t.Fatal("Watch returned non-nil error, want error")
   120  	}
   121  
   122  	// Setting a value makes Watch return again.
   123  	fake.Set(&state{val: "hello"})
   124  	if _, err := v.Watch(ctx); err != nil {
   125  		t.Fatalf("Watch returned error %v, want nil", err)
   126  	}
   127  
   128  	// Make a few updates. Each of these will try to write to the nextWatchCh,
   129  	// but we should only keep the latest one.
   130  	fake.Set(&state{val: "hello1"})
   131  	fake.Set(&state{val: "hello2"})
   132  	fake.Set(&state{val: "hello3"})
   133  	fake.Set(&state{val: "hello4"})
   134  	fake.Set(&state{val: "hello5"})
   135  	// Wait until we're sure the last one has been received.
   136  	for {
   137  		snap, err := v.Latest(ctx)
   138  		if err != nil {
   139  			t.Errorf("got unexpected error from Latest: %v", err)
   140  		}
   141  		if snap.Value == "hello5" {
   142  			break
   143  		}
   144  	}
   145  
   146  	// Watch should get the last one, hello5.
   147  	if snap, err := v.Watch(ctx); err != nil {
   148  		t.Fatalf("Watch returned error %v, want nil", err)
   149  	} else if snap.Value != "hello5" {
   150  		t.Errorf("Watch got %v, want hello5", snap.Value)
   151  	}
   152  
   153  	// And the next call should block.
   154  	ctx2, cancel = context.WithTimeout(ctx, blockingCheckDelay)
   155  	defer cancel()
   156  	if _, err := v.Watch(ctx2); err == nil {
   157  		t.Errorf("Watch after no change in good value should block: got nil err, want err")
   158  	}
   159  	if ctx2.Err() == nil {
   160  		t.Error("Watch after no change in good value should block")
   161  	}
   162  
   163  	// Ensure a blocking Watch returns when a new value arrives.
   164  	// Wait blockingCheckDelay to give some time to be blocking in Watch.
   165  	// There's no guarantee it will get there, but if Watch doesn't handle
   166  	// cancelation properly, then the test will fail whenever it does get there,
   167  	// so at least we'll observe a flaky test.
   168  	time.AfterFunc(blockingCheckDelay, func() { fake.Set(&state{val: "hello6"}) })
   169  	if snap, err := v.Watch(ctx); err != nil {
   170  		t.Errorf("Watch interrupted by new value returned %v, want nil", err)
   171  	} else if snap.Value != "hello6" {
   172  		t.Errorf("Watch got %v, want hello6", snap.Value)
   173  	}
   174  
   175  	// Similarly, ensure a blocking Watch is interrupted by Close.
   176  	time.AfterFunc(blockingCheckDelay, func() {
   177  		if err := v.Close(); err != nil {
   178  			t.Error(err)
   179  		}
   180  	})
   181  	if _, err := v.Watch(ctx); err != ErrClosed {
   182  		t.Errorf("Watch interrupted by Close returned %v, want ErrClosed", err)
   183  	}
   184  
   185  	// Watch should now return ErrClosed.
   186  	if _, err := v.Watch(ctx); err != ErrClosed {
   187  		t.Errorf("Watch after Close returned %v, want ErrClosed", err)
   188  	}
   189  }
   190  
   191  func TestVariable_Latest(t *testing.T) {
   192  	const content1, content2 = "foo", "bar"
   193  	const numGoroutines = 10
   194  	ctx := context.Background()
   195  
   196  	fake := &fakeWatcher{}
   197  	v := New(fake)
   198  
   199  	// Not healthy at startup.
   200  	if v.CheckHealth() == nil {
   201  		t.Error("got nil from CheckHealth, want error")
   202  	}
   203  
   204  	// Latest should block until the context is done, as there's no value.
   205  	ctx2, cancel := context.WithTimeout(ctx, blockingCheckDelay)
   206  	defer cancel()
   207  	if _, err := v.Latest(ctx2); err == nil {
   208  		t.Errorf("Latest with no value yet should block: got nil err, want err")
   209  	}
   210  	if ctx2.Err() == nil {
   211  		t.Error("Latest with no value yet should block")
   212  	}
   213  	// And we're not healthy.
   214  	if v.CheckHealth() == nil {
   215  		t.Error("got nil from CheckHealth, want error")
   216  	}
   217  
   218  	// Call Latest concurrently. There's still no value.
   219  	var wg sync.WaitGroup
   220  	wg.Add(numGoroutines)
   221  	for i := 0; i < numGoroutines; i++ {
   222  		go func() {
   223  			ctx2, cancel := context.WithTimeout(ctx, blockingCheckDelay)
   224  			cancel()
   225  			if _, err := v.Latest(ctx2); err == nil {
   226  				t.Errorf("Latest with no value yet: got nil err, want err")
   227  			}
   228  			wg.Done()
   229  		}()
   230  	}
   231  	wg.Wait()
   232  
   233  	// Set an error value. Latest should still block.
   234  	fake.Set(&state{err: errFake})
   235  	ctx2, cancel = context.WithTimeout(ctx, blockingCheckDelay)
   236  	defer cancel()
   237  	if _, err := v.Latest(ctx2); err == nil {
   238  		t.Errorf("Latest with error value should block: got nil err, want err")
   239  	}
   240  	if ctx2.Err() == nil {
   241  		t.Error("Latest with error value should block")
   242  	}
   243  	// And we're still not healthy.
   244  	if v.CheckHealth() == nil {
   245  		t.Error("got nil from CheckHealth, want error")
   246  	}
   247  
   248  	// Call Latest concurrently, only exiting each goroutine when they
   249  	// see the content1 value.
   250  	wg.Add(numGoroutines)
   251  	for i := 0; i < numGoroutines; i++ {
   252  		go func() {
   253  			for {
   254  				val, err := v.Latest(ctx)
   255  				if err != nil {
   256  					continue
   257  				}
   258  				if val.Value != content1 {
   259  					t.Errorf("got %v want %s", val, content1)
   260  				}
   261  				wg.Done()
   262  				return
   263  			}
   264  		}()
   265  	}
   266  	// Set a good value, after a small delay to give the goroutines a chance
   267  	// to get into Latest.
   268  	time.Sleep(blockingCheckDelay)
   269  	fake.Set(&state{val: content1})
   270  	wg.Wait()
   271  	// And now we're healthy.
   272  	if err := v.CheckHealth(); err != nil {
   273  		t.Errorf("got %v from CheckHealth, want nil", err)
   274  	}
   275  
   276  	// Set a different value. At some point after this, Latest should start
   277  	// returning a Snapshot with Value set to content2.
   278  	fake.Set(&state{val: content2})
   279  
   280  	// Call Latest concurrently, only exiting each goroutine when they
   281  	// see the content2 value.
   282  	wg.Add(numGoroutines)
   283  	for i := 0; i < numGoroutines; i++ {
   284  		go func() {
   285  			for {
   286  				val, err := v.Latest(ctx)
   287  				if err != nil {
   288  					// Errors are unexpected at this point.
   289  					t.Error(err)
   290  				}
   291  				if val.Value == content1 {
   292  					// Still seeing the old value.
   293  					continue
   294  				}
   295  				if val.Value != content2 {
   296  					t.Errorf("got %v want %s", val, content2)
   297  				}
   298  				wg.Done()
   299  				return
   300  			}
   301  		}()
   302  	}
   303  	wg.Wait()
   304  
   305  	// Set an error value. Latest should still return content2.
   306  	fake.Set(&state{err: errFake})
   307  
   308  	// Call Latest concurrently. The test will be flaky if some of them
   309  	// start getting errors.
   310  	wg.Add(numGoroutines)
   311  	for i := 0; i < numGoroutines; i++ {
   312  		go func() {
   313  			for {
   314  				val, err := v.Latest(ctx)
   315  				if err != nil {
   316  					// Errors are unexpected at this point.
   317  					t.Error(err)
   318  				} else if val.Value != content2 {
   319  					t.Errorf("got %v want %s", val.Value, content2)
   320  				}
   321  				wg.Done()
   322  				return
   323  			}
   324  		}()
   325  	}
   326  	wg.Wait()
   327  
   328  	// Still healthy.
   329  	if err := v.CheckHealth(); err != nil {
   330  		t.Errorf("got %v from CheckHealth, want nil", err)
   331  	}
   332  
   333  	// Close the variable.
   334  	if err := v.Close(); err != nil {
   335  		t.Error(err)
   336  	}
   337  
   338  	// Latest should now return ErrClosed.
   339  	if _, err := v.Latest(ctx); err != ErrClosed {
   340  		t.Errorf("Latest after close returned %v, want ErrClosed", err)
   341  	}
   342  	// Unhealthy now.
   343  	if err := v.CheckHealth(); err != ErrClosed {
   344  		t.Errorf("got %v from CheckHealth, want ErrClosed", err)
   345  	}
   346  }
   347  
   348  // Tests that Latest is interrupted by Close.
   349  func TestVariable_LatestBlockedDuringClose(t *testing.T) {
   350  	fake := &fakeWatcher{}
   351  	v := New(fake)
   352  
   353  	ctx := context.Background()
   354  
   355  	// Wait blockingCheckDelay to give some time to be blocking in Latest.
   356  	// There's no guarantee it will get there, but if Latest doesn't handle
   357  	// cancelation properly, then the test will fail whenever it does get there,
   358  	// so at least we'll observe a flaky test.
   359  	time.AfterFunc(blockingCheckDelay, func() {
   360  		if err := v.Close(); err != nil {
   361  			t.Error(err)
   362  		}
   363  	})
   364  	if _, err := v.Latest(ctx); err != ErrClosed {
   365  		t.Errorf("Latest interrupted by Close got %v, want ErrClosed", err)
   366  	}
   367  
   368  	// Calling Close again should return ErrClosed.
   369  	if err := v.Close(); err != ErrClosed {
   370  		t.Errorf("calling Close 2x returned %v, want ErrClosed", err)
   371  	}
   372  }
   373  
   374  var errFake = errors.New("fake")
   375  
   376  // erroringWatcher implements driver.Watcher.
   377  // WatchVariable always returns a state with errFake, and Close
   378  // always returns errFake.
   379  type erroringWatcher struct {
   380  	driver.Watcher
   381  }
   382  
   383  func (b *erroringWatcher) WatchVariable(ctx context.Context, prev driver.State) (driver.State, time.Duration) {
   384  	return &state{err: errFake}, 0
   385  }
   386  
   387  func (b *erroringWatcher) Close() error {
   388  	return errFake
   389  }
   390  
   391  func (b *erroringWatcher) ErrorCode(err error) gcerrors.ErrorCode {
   392  	return gcerrors.Internal
   393  }
   394  
   395  // TestErrorsAreWrapped tests that all errors returned from the driver are
   396  // wrapped exactly once by the portable type.
   397  func TestErrorsAreWrapped(t *testing.T) {
   398  	ctx := context.Background()
   399  	v := New(&erroringWatcher{})
   400  
   401  	// verifyWrap ensures that err is wrapped exactly once.
   402  	verifyWrap := func(description string, err error) {
   403  		if unwrapped, ok := err.(*gcerr.Error); !ok {
   404  			t.Errorf("%s: not wrapped: %v", description, err)
   405  		} else if du, ok := unwrapped.Unwrap().(*gcerr.Error); ok {
   406  			t.Errorf("%s: double wrapped: %v", description, du)
   407  		}
   408  	}
   409  
   410  	_, err := v.Watch(ctx)
   411  	verifyWrap("Watch", err)
   412  
   413  	err = v.Close()
   414  	verifyWrap("Close", err)
   415  }
   416  
   417  var (
   418  	testOpenOnce sync.Once
   419  	testOpenGot  *url.URL
   420  )
   421  
   422  func TestURLMux(t *testing.T) {
   423  	ctx := context.Background()
   424  
   425  	mux := new(URLMux)
   426  	fake := &fakeOpener{}
   427  	mux.RegisterVariable("foo", fake)
   428  	mux.RegisterVariable("err", fake)
   429  
   430  	if diff := cmp.Diff(mux.VariableSchemes(), []string{"err", "foo"}); diff != "" {
   431  		t.Errorf("Schemes: %s", diff)
   432  	}
   433  	if !mux.ValidVariableScheme("foo") || !mux.ValidVariableScheme("err") {
   434  		t.Errorf("ValidVariableScheme didn't return true for valid scheme")
   435  	}
   436  	if mux.ValidVariableScheme("foo2") || mux.ValidVariableScheme("http") {
   437  		t.Errorf("ValidVariableScheme didn't return false for invalid scheme")
   438  	}
   439  
   440  	for _, tc := range []struct {
   441  		name    string
   442  		url     string
   443  		wantErr bool
   444  	}{
   445  		{
   446  			name:    "empty URL",
   447  			wantErr: true,
   448  		},
   449  		{
   450  			name:    "invalid URL",
   451  			url:     ":foo",
   452  			wantErr: true,
   453  		},
   454  		{
   455  			name:    "invalid URL no scheme",
   456  			url:     "foo",
   457  			wantErr: true,
   458  		},
   459  		{
   460  			name:    "unregistered scheme",
   461  			url:     "bar://myvar",
   462  			wantErr: true,
   463  		},
   464  		{
   465  			name:    "func returns error",
   466  			url:     "err://myvar",
   467  			wantErr: true,
   468  		},
   469  		{
   470  			name: "no query options",
   471  			url:  "foo://myvar",
   472  		},
   473  		{
   474  			name: "empty query options",
   475  			url:  "foo://myvar?",
   476  		},
   477  		{
   478  			name: "query options",
   479  			url:  "foo://myvar?aAa=bBb&cCc=dDd",
   480  		},
   481  		{
   482  			name: "multiple query options",
   483  			url:  "foo://myvar?x=a&x=b&x=c",
   484  		},
   485  		{
   486  			name: "fancy var name",
   487  			url:  "foo:///foo/bar/baz",
   488  		},
   489  		{
   490  			name: "using api scheme prefix",
   491  			url:  "runtimevar+foo:///foo/bar/baz",
   492  		},
   493  		{
   494  			name: "using api+type scheme prefix",
   495  			url:  "runtimevar+variable+foo:///foo/bar/baz",
   496  		},
   497  	} {
   498  		t.Run(tc.name, func(t *testing.T) {
   499  			_, gotErr := mux.OpenVariable(ctx, tc.url)
   500  			if (gotErr != nil) != tc.wantErr {
   501  				t.Fatalf("got err %v, want error %v", gotErr, tc.wantErr)
   502  			}
   503  			if gotErr != nil {
   504  				return
   505  			}
   506  			if got := fake.u.String(); got != tc.url {
   507  				t.Errorf("got %q want %q", got, tc.url)
   508  			}
   509  			// Repeat with OpenVariableURL.
   510  			parsed, err := url.Parse(tc.url)
   511  			if err != nil {
   512  				t.Fatal(err)
   513  			}
   514  			_, gotErr = mux.OpenVariableURL(ctx, parsed)
   515  			if gotErr != nil {
   516  				t.Fatalf("got err %v, want nil", gotErr)
   517  			}
   518  			if got := fake.u.String(); got != tc.url {
   519  				t.Errorf("got %q want %q", got, tc.url)
   520  			}
   521  		})
   522  	}
   523  }
   524  
   525  type fakeOpener struct {
   526  	u *url.URL // last url passed to OpenVariableURL
   527  }
   528  
   529  func (o *fakeOpener) OpenVariableURL(ctx context.Context, u *url.URL) (*Variable, error) {
   530  	if u.Scheme == "err" {
   531  		return nil, errors.New("fail")
   532  	}
   533  	o.u = u
   534  	return nil, nil
   535  }
   536  
   537  func TestDecoder(t *testing.T) {
   538  	type Struct struct {
   539  		FieldA string
   540  		FieldB map[string]interface{}
   541  	}
   542  
   543  	num := 4321
   544  	numptr := &num
   545  	str := "boring string"
   546  	strptr := &str
   547  
   548  	inputs := []interface{}{
   549  		str,
   550  		strptr,
   551  		num,
   552  		numptr,
   553  		100.1,
   554  		Struct{
   555  			FieldA: "hello",
   556  			FieldB: map[string]interface{}{
   557  				"hello": "world",
   558  			},
   559  		},
   560  		&Struct{
   561  			FieldA: "world",
   562  		},
   563  		map[string]string{
   564  			"slice": "pizza",
   565  		},
   566  		&map[string]interface{}{},
   567  		[]string{"hello", "world"},
   568  		&[]int{1, 0, 1},
   569  		[...]float64{3.1415},
   570  		&[...]int64{4, 5, 6},
   571  	}
   572  
   573  	for _, tc := range []struct {
   574  		desc     string
   575  		encodeFn func(interface{}) ([]byte, error)
   576  		decodeFn Decode
   577  	}{
   578  		{
   579  			desc:     "JSON",
   580  			encodeFn: json.Marshal,
   581  			decodeFn: JSONDecode,
   582  		},
   583  		{
   584  			desc:     "Gob",
   585  			encodeFn: gobMarshal,
   586  			decodeFn: GobDecode,
   587  		},
   588  	} {
   589  		for i, input := range inputs {
   590  			t.Run(fmt.Sprintf("%s_%d", tc.desc, i), func(t *testing.T) {
   591  				decoder := NewDecoder(input, tc.decodeFn)
   592  				b, err := tc.encodeFn(input)
   593  				if err != nil {
   594  					t.Fatalf("marshal error %v", err)
   595  				}
   596  				got, err := decoder.Decode(context.Background(), b)
   597  				if err != nil {
   598  					t.Fatalf("parse input\n%s\nerror: %v", string(b), err)
   599  				}
   600  				if reflect.TypeOf(got) != reflect.TypeOf(input) {
   601  					t.Errorf("type mismatch got %T, want %T", got, input)
   602  				}
   603  				if diff := cmp.Diff(got, input); diff != "" {
   604  					t.Errorf("value diff:\n%v", diff)
   605  				}
   606  			})
   607  		}
   608  	}
   609  }
   610  
   611  func gobMarshal(v interface{}) ([]byte, error) {
   612  	var buf bytes.Buffer
   613  	if err := gob.NewEncoder(&buf).Encode(v); err != nil {
   614  		return nil, err
   615  	}
   616  	return buf.Bytes(), nil
   617  }
   618  
   619  func TestStringDecoder(t *testing.T) {
   620  	input := "hello world"
   621  	got, err := StringDecoder.Decode(context.Background(), []byte(input))
   622  	if err != nil {
   623  		t.Fatalf("error: %v", err)
   624  	}
   625  	if input != got.(string) {
   626  		t.Errorf("output got %v, want %q", got, input)
   627  	}
   628  }
   629  
   630  func TestBytesDecoder(t *testing.T) {
   631  	input := []byte("hello world")
   632  	got, err := BytesDecoder.Decode(context.Background(), input)
   633  	if err != nil {
   634  		t.Fatalf("error: %v", err)
   635  	}
   636  	if diff := cmp.Diff(got, input); diff != "" {
   637  		t.Errorf("output got %v, want %q", got, input)
   638  	}
   639  }
   640  
   641  func TestDecryptDecoder(t *testing.T) {
   642  	ctx := context.Background()
   643  	secretKey, err := localsecrets.NewRandomKey()
   644  	if err != nil {
   645  		t.Fatal(err)
   646  	}
   647  	keeper := localsecrets.NewKeeper(secretKey)
   648  
   649  	tests := []struct {
   650  		desc      string
   651  		in        interface{}
   652  		encodeFn  func(interface{}) ([]byte, error)
   653  		postDecFn Decode
   654  	}{
   655  		{
   656  			desc:     "Bytes",
   657  			in:       []byte("hello world"),
   658  			encodeFn: func(obj interface{}) ([]byte, error) { return obj.([]byte), nil },
   659  		},
   660  		{
   661  			desc:      "String",
   662  			in:        "hello world",
   663  			encodeFn:  func(obj interface{}) ([]byte, error) { return []byte(obj.(string)), nil },
   664  			postDecFn: StringDecode,
   665  		},
   666  		{
   667  			desc: "JSON",
   668  			in: map[string]string{
   669  				"slice": "pizza",
   670  			},
   671  			encodeFn:  json.Marshal,
   672  			postDecFn: JSONDecode,
   673  		},
   674  	}
   675  	for _, tc := range tests {
   676  		t.Run(tc.desc, func(t *testing.T) {
   677  			decoder := NewDecoder(tc.in, DecryptDecode(keeper, tc.postDecFn))
   678  
   679  			b, err := tc.encodeFn(tc.in)
   680  			if err != nil {
   681  				t.Fatalf("encode error %v", err)
   682  			}
   683  			encrypted, err := keeper.Encrypt(ctx, b)
   684  			if err != nil {
   685  				t.Fatalf("encrypt error: %v", err)
   686  			}
   687  
   688  			got, err := decoder.Decode(ctx, encrypted)
   689  			if err != nil {
   690  				t.Fatalf("parse input\n%s\nerror: %v", string(b), err)
   691  			}
   692  			if reflect.TypeOf(got) != reflect.TypeOf(tc.in) {
   693  				t.Errorf("type mismatch got %T, want %T", got, tc.in)
   694  			}
   695  			if diff := cmp.Diff(got, tc.in); diff != "" {
   696  				t.Errorf("value diff:\n%v", diff)
   697  			}
   698  		})
   699  	}
   700  }