github.com/blend/go-sdk@v1.20240719.1/ex/as_test.go (about)

     1  /*
     2  
     3  Copyright (c) 2024 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package ex
     9  
    10  import (
    11  	"errors"
    12  	"fmt"
    13  	"testing"
    14  
    15  	"github.com/blend/go-sdk/assert"
    16  )
    17  
    18  type customError struct {
    19  	err error
    20  }
    21  
    22  func (e *customError) Unwrap() error {
    23  	return e.err
    24  }
    25  
    26  func (e *customError) Error() string {
    27  	return e.err.Error()
    28  }
    29  
    30  func TestAs(t *testing.T) {
    31  	// base sentinel errors to check against
    32  	baseSentinel := &Ex{Class: Class("sentinel")}
    33  	baseSentinelAlt := &Ex{Class: Class("sentinel alt")}
    34  
    35  	exSentinelErr := New("sentinel")
    36  	stdLibSentinelErr := errors.New("sentinel")
    37  	nonPointerExErr := Ex{Class: Class("sentinel")}
    38  	customErrWrappingEx := customError{err: exSentinelErr}
    39  	customErrWrappingstdLibErr := customError{err: stdLibSentinelErr}
    40  	customDeepWrappedErr := customError{
    41  		err: fmt.Errorf(
    42  			"Top of deep error: %w",
    43  			fmt.Errorf(
    44  				"Level 1 of deep error: %w",
    45  				&customError{err: New(exSentinelErr)},
    46  			),
    47  		),
    48  	}
    49  
    50  	// Note: Multi's Unwrap method converts all errors in the error chain to *ex.Ex.
    51  	multiErrorWithStdLibFirst := Append(stdLibSentinelErr, baseSentinelAlt)
    52  	multiErrorWithAltFirst := Append(baseSentinelAlt, exSentinelErr)
    53  	multiErrorWithSentinelFirst := Append(exSentinelErr, baseSentinelAlt)
    54  
    55  	tests := []struct {
    56  		name       string
    57  		candidate  error
    58  		expectedEx *Ex
    59  	}{
    60  		{
    61  			name:       "stdlib error is not Ex pointer",
    62  			candidate:  stdLibSentinelErr,
    63  			expectedEx: nil,
    64  		},
    65  		{
    66  			name:       "error from New is Ex pointer",
    67  			candidate:  exSentinelErr,
    68  			expectedEx: baseSentinel,
    69  		},
    70  		{
    71  			name:       "non pointer Ex is resolved to Ex pointer",
    72  			candidate:  &nonPointerExErr,
    73  			expectedEx: baseSentinel,
    74  		},
    75  		{
    76  			name:       "Handles custom errors that wrap Ex pointer",
    77  			candidate:  &customErrWrappingEx,
    78  			expectedEx: baseSentinel,
    79  		},
    80  		{
    81  			name:       "wrapped stdlib error is no Ex pointer",
    82  			candidate:  &customErrWrappingstdLibErr,
    83  			expectedEx: nil,
    84  		},
    85  		{
    86  			name:       "deeply nested Ex pointer is extracted",
    87  			candidate:  &customDeepWrappedErr,
    88  			expectedEx: baseSentinel,
    89  		},
    90  		{
    91  			name:       "Returns first Ex in Multi error chain",
    92  			candidate:  multiErrorWithAltFirst,
    93  			expectedEx: baseSentinelAlt,
    94  		},
    95  		{
    96  			name:       "Returns first Ex in Multi error chain alt",
    97  			candidate:  multiErrorWithSentinelFirst,
    98  			expectedEx: baseSentinel,
    99  		},
   100  		{
   101  			name:       "Returns first error as Ex in Multi error chain",
   102  			candidate:  multiErrorWithStdLibFirst,
   103  			expectedEx: baseSentinel,
   104  		},
   105  	}
   106  
   107  	for _, test := range tests {
   108  		test := test
   109  		t.Run(test.name, func(t *testing.T) {
   110  			t.Parallel()
   111  			its := assert.New(t)
   112  			maybeEx := As(test.candidate)
   113  			if test.expectedEx == nil {
   114  				its.Nil(maybeEx)
   115  				return
   116  			}
   117  			its.NotNil(maybeEx)
   118  			its.Equal(test.expectedEx.Error(), maybeEx.Error())
   119  		})
   120  	}
   121  }