github.com/blend/go-sdk@v1.20240719.1/ex/ex_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 "encoding/json" 12 "errors" 13 "fmt" 14 "strings" 15 "testing" 16 17 "github.com/blend/go-sdk/assert" 18 ) 19 20 func TestNewOfString(t *testing.T) { 21 a := assert.New(t) 22 ex := As(New("this is a test")) 23 a.Equal("this is a test", fmt.Sprintf("%v", ex)) 24 a.NotNil(ex.StackTrace) 25 a.Nil(ex.Inner) 26 } 27 28 func TestNewOfError(t *testing.T) { 29 a := assert.New(t) 30 31 err := errors.New("This is an error") 32 wrappedErr := New(err) 33 a.NotNil(wrappedErr) 34 typedWrapped := As(wrappedErr) 35 a.NotNil(typedWrapped) 36 a.Equal("This is an error", fmt.Sprintf("%v", typedWrapped)) 37 } 38 39 func TestNewOfException(t *testing.T) { 40 a := assert.New(t) 41 ex := New(Class("This is an exception")) 42 wrappedEx := New(ex) 43 a.NotNil(wrappedEx) 44 typedWrappedEx := As(wrappedEx) 45 a.Equal("This is an exception", fmt.Sprintf("%v", typedWrappedEx)) 46 a.Equal(ex, typedWrappedEx) 47 } 48 49 func TestNewOfNil(t *testing.T) { 50 a := assert.New(t) 51 52 shouldBeNil := New(nil) 53 a.Nil(shouldBeNil) 54 a.Equal(nil, shouldBeNil) 55 a.True(nil == shouldBeNil) 56 } 57 58 func TestNewOfTypedNil(t *testing.T) { 59 a := assert.New(t) 60 61 var nilError error 62 a.Nil(nilError) 63 a.Equal(nil, nilError) 64 65 shouldBeNil := New(nilError) 66 a.Nil(shouldBeNil) 67 a.True(shouldBeNil == nil) 68 } 69 70 func TestNewOfReturnedNil(t *testing.T) { 71 a := assert.New(t) 72 73 returnsNil := func() error { 74 return nil 75 } 76 77 shouldBeNil := New(returnsNil()) 78 a.Nil(shouldBeNil) 79 a.True(shouldBeNil == nil) 80 81 returnsTypedNil := func() error { 82 return New(nil) 83 } 84 85 shouldAlsoBeNil := returnsTypedNil() 86 a.Nil(shouldAlsoBeNil) 87 a.True(shouldAlsoBeNil == nil) 88 } 89 90 func TestError(t *testing.T) { 91 a := assert.New(t) 92 93 ex := New(Class("this is a test")) 94 message := ex.Error() 95 a.NotEmpty(message) 96 } 97 98 func TestErrorOptions(t *testing.T) { 99 a := assert.New(t) 100 101 ex := New(Class("this is a test"), OptMessage("foo")) 102 message := ex.Error() 103 a.NotEmpty(message) 104 105 typed := As(ex) 106 a.NotNil(typed) 107 a.Equal("foo", typed.Message) 108 } 109 110 func TestCallers(t *testing.T) { 111 a := assert.New(t) 112 113 callStack := func() StackTrace { return Callers(DefaultStartDepth) }() 114 115 a.NotNil(callStack) 116 callstackStr := callStack.String() 117 a.True(strings.Contains(callstackStr, "TestCallers"), callstackStr) 118 } 119 120 func TestExceptionFormatters(t *testing.T) { 121 assert := assert.New(t) 122 123 // test the "%v" formatter with just the exception class. 124 class := &Ex{Class: Class("this is a test")} 125 assert.Equal("this is a test", fmt.Sprintf("%v", class)) 126 127 classAndMessage := &Ex{Class: Class("foo"), Message: "bar"} 128 assert.Equal("foo; bar", fmt.Sprintf("%v", classAndMessage)) 129 } 130 131 func TestSerializeExamples(t *testing.T) { 132 it := assert.New(t) 133 134 n := (*Ex)(nil) 135 e := Ex{} 136 c1 := Ex{Class: Class("cls")} 137 c2 := Ex{Class: fmt.Errorf("cls")} 138 m := Ex{Message: "msg"} 139 i1 := Ex{Inner: Class("inner")} 140 i2 := Ex{Inner: &Ex{Class: Class("inner")}} 141 i3 := Ex{Inner: &Ex{Message: "inner"}} 142 i4 := Ex{Inner: fmt.Errorf("inner")} 143 s := Ex{StackTrace: StackStrings{"stack"}} 144 mc1 := Ex{Message: m.Message, Class: c1.Class} 145 mc2 := Ex{Message: m.Message, Class: c2.Class} 146 msc1 := Ex{Message: m.Message, Class: c1.Class, StackTrace: s.StackTrace} 147 msc2 := Ex{Message: m.Message, Class: c2.Class, StackTrace: s.StackTrace} 148 149 // Error ((*Ex).Error) 150 // it.Equal("<nil>", n.Error()) // panics 151 // it.Equal("", e.Error()) // panics 152 it.Equal("cls", c1.Error()) 153 it.Equal("cls", c2.Error()) 154 // it.Equal(" msg", m.Error()) // panics 155 // it.Equal("", i1.Error()) // panics 156 // it.Equal("", i2.Error()) // panics 157 // it.Equal("", i3.Error()) // panics 158 // it.Equal("", i4.Error()) // panics 159 // it.Equal(" \nstack", s.Error()) // panics 160 it.Equal("cls", mc1.Error()) 161 it.Equal("cls", mc2.Error()) 162 it.Equal("cls", msc1.Error()) 163 it.Equal("cls", msc2.Error()) 164 165 // Stringer ((*Ex).String) 166 // it.Equal("<nil>", n.String()) // panics 167 it.Equal("", e.String()) 168 it.Equal("cls", c1.String()) 169 it.Equal("cls", c2.String()) 170 it.Equal(" msg", m.String()) 171 it.Equal("", i1.String()) 172 it.Equal("", i2.String()) 173 it.Equal("", i3.String()) 174 it.Equal("", i4.String()) 175 it.Equal(" \nstack", s.String()) 176 it.Equal("cls msg", mc1.String()) 177 it.Equal("cls msg", mc2.String()) 178 it.Equal("cls msg \nstack", msc1.String()) 179 it.Equal("cls msg \nstack", msc2.String()) 180 181 // JSON ((*Ex).MarshalJSON) 182 var b []byte 183 var err error 184 // b, err = n.MarshalJSON() // panics 185 // b, err = e.MarshalJSON() // panics 186 b, err = c1.MarshalJSON() 187 it.Nil(err) 188 it.Equal(`{"Class":"cls","Message":""}`, string(b)) 189 b, err = c2.MarshalJSON() 190 it.Nil(err) 191 it.Equal(`{"Class":"cls","Message":""}`, string(b)) 192 // b, err = m.MarshalJSON() // panics 193 // b, err = i1.MarshalJSON() // panics 194 // b, err = i2.MarshalJSON() // panics 195 // b, err = i3.MarshalJSON() // panics 196 // b, err = i4.MarshalJSON() // panics 197 // b, err = s.MarshalJSON() // panics 198 b, err = mc1.MarshalJSON() 199 it.Nil(err) 200 it.Equal(`{"Class":"cls","Message":"msg"}`, string(b)) 201 b, err = mc2.MarshalJSON() 202 it.Nil(err) 203 it.Equal(`{"Class":"cls","Message":"msg"}`, string(b)) 204 b, err = msc1.MarshalJSON() 205 it.Nil(err) 206 it.Equal(`{"Class":"cls","Message":"msg","StackTrace":["stack"]}`, string(b)) 207 b, err = msc2.MarshalJSON() 208 it.Nil(err) 209 it.Equal(`{"Class":"cls","Message":"msg","StackTrace":["stack"]}`, string(b)) 210 211 // Format: Ex -> v 212 it.Equal("<nil>", fmt.Sprintf("%v", n)) 213 it.Equal("{<nil> <nil> <nil>}", fmt.Sprintf("%v", e)) 214 it.Equal("{cls <nil> <nil>}", fmt.Sprintf("%v", c1)) 215 it.Equal("{cls <nil> <nil>}", fmt.Sprintf("%v", c2)) 216 it.Equal("{<nil> msg <nil> <nil>}", fmt.Sprintf("%v", m)) 217 it.Equal("{<nil> inner <nil>}", fmt.Sprintf("%v", i1)) 218 it.Equal("{<nil> inner <nil>}", fmt.Sprintf("%v", i2)) 219 it.Equal("{<nil> ; inner <nil>}", fmt.Sprintf("%v", i3)) 220 it.Equal("{<nil> inner <nil>}", fmt.Sprintf("%v", i4)) 221 it.Equal("{<nil> <nil> \nstack}", fmt.Sprintf("%v", s)) 222 it.Equal("{cls msg <nil> <nil>}", fmt.Sprintf("%v", mc1)) 223 it.Equal("{cls msg <nil> <nil>}", fmt.Sprintf("%v", mc2)) 224 it.Equal("{cls msg <nil> \nstack}", fmt.Sprintf("%v", msc1)) 225 it.Equal("{cls msg <nil> \nstack}", fmt.Sprintf("%v", msc2)) 226 227 // Format: *Ex -> v 228 it.Equal("", fmt.Sprintf("%v", &e)) 229 it.Equal("cls", fmt.Sprintf("%v", &c1)) 230 it.Equal("cls", fmt.Sprintf("%v", &c2)) 231 it.Equal("; msg", fmt.Sprintf("%v", &m)) 232 it.Equal("\ninner", fmt.Sprintf("%v", &i1)) 233 it.Equal("\ninner", fmt.Sprintf("%v", &i2)) 234 it.Equal("\n; inner", fmt.Sprintf("%v", &i3)) 235 it.Equal("\ninner", fmt.Sprintf("%v", &i4)) 236 it.Equal("", fmt.Sprintf("%v", &s)) 237 it.Equal("cls; msg", fmt.Sprintf("%v", &mc1)) 238 it.Equal("cls; msg", fmt.Sprintf("%v", &mc2)) 239 it.Equal("cls; msg", fmt.Sprintf("%v", &msc1)) 240 it.Equal("cls; msg", fmt.Sprintf("%v", &msc2)) 241 242 // Format: Ex -> +v 243 it.Equal("<nil>", fmt.Sprintf("%+v", n)) 244 it.Equal("{Class:<nil> Message: Inner:<nil> StackTrace:<nil>}", fmt.Sprintf("%+v", e)) 245 it.Equal("{Class:cls Message: Inner:<nil> StackTrace:<nil>}", fmt.Sprintf("%+v", c1)) 246 it.Equal("{Class:cls Message: Inner:<nil> StackTrace:<nil>}", fmt.Sprintf("%+v", c2)) 247 it.Equal("{Class:<nil> Message:msg Inner:<nil> StackTrace:<nil>}", fmt.Sprintf("%+v", m)) 248 it.Equal("{Class:<nil> Message: Inner:inner StackTrace:<nil>}", fmt.Sprintf("%+v", i1)) 249 it.Equal("{Class:<nil> Message: Inner:inner StackTrace:<nil>}", fmt.Sprintf("%+v", i2)) 250 it.Equal("{Class:<nil> Message: Inner:; inner StackTrace:<nil>}", fmt.Sprintf("%+v", i3)) 251 it.Equal("{Class:<nil> Message: Inner:inner StackTrace:<nil>}", fmt.Sprintf("%+v", i4)) 252 it.Equal("{Class:<nil> Message: Inner:<nil> StackTrace:\nstack}", fmt.Sprintf("%+v", s)) 253 it.Equal("{Class:cls Message:msg Inner:<nil> StackTrace:<nil>}", fmt.Sprintf("%+v", mc1)) 254 it.Equal("{Class:cls Message:msg Inner:<nil> StackTrace:<nil>}", fmt.Sprintf("%+v", mc2)) 255 it.Equal("{Class:cls Message:msg Inner:<nil> StackTrace:\nstack}", fmt.Sprintf("%+v", msc1)) 256 it.Equal("{Class:cls Message:msg Inner:<nil> StackTrace:\nstack}", fmt.Sprintf("%+v", msc2)) 257 258 // Format: *Ex -> +v 259 it.Equal("", fmt.Sprintf("%+v", &e)) 260 it.Equal("cls", fmt.Sprintf("%+v", &c1)) 261 it.Equal("cls", fmt.Sprintf("%+v", &c2)) 262 it.Equal("; msg", fmt.Sprintf("%+v", &m)) 263 it.Equal("\ninner", fmt.Sprintf("%+v", &i1)) 264 it.Equal("\ninner", fmt.Sprintf("%+v", &i2)) 265 it.Equal("\n; inner", fmt.Sprintf("%+v", &i3)) 266 it.Equal("\ninner", fmt.Sprintf("%+v", &i4)) 267 it.Equal("\nstack", fmt.Sprintf("%+v", &s)) 268 it.Equal("cls; msg", fmt.Sprintf("%+v", &mc1)) 269 it.Equal("cls; msg", fmt.Sprintf("%+v", &mc2)) 270 it.Equal("cls; msg\nstack", fmt.Sprintf("%+v", &msc1)) 271 it.Equal("cls; msg\nstack", fmt.Sprintf("%+v", &msc2)) 272 273 // Format: Ex -> #v 274 it.Equal("<nil>", fmt.Sprintf("%#v", n)) 275 it.Equal(`ex.Ex{Class:error(nil), Message:"", Inner:error(nil), StackTrace:ex.StackTrace(nil)}`, fmt.Sprintf("%#v", e)) 276 it.Equal(`ex.Ex{Class:"cls", Message:"", Inner:error(nil), StackTrace:ex.StackTrace(nil)}`, fmt.Sprintf("%#v", c1)) 277 it.HasPrefix(fmt.Sprintf("%#v", c2), `ex.Ex{Class:(*errors.errorString)(0x`) 278 it.HasSuffix(fmt.Sprintf("%#v", c2), `), Message:"", Inner:error(nil), StackTrace:ex.StackTrace(nil)}`) 279 it.Equal(`ex.Ex{Class:error(nil), Message:"msg", Inner:error(nil), StackTrace:ex.StackTrace(nil)}`, fmt.Sprintf("%#v", m)) 280 it.Equal(`ex.Ex{Class:error(nil), Message:"", Inner:"inner", StackTrace:ex.StackTrace(nil)}`, fmt.Sprintf("%#v", i1)) 281 it.Equal(`ex.Ex{Class:error(nil), Message:"", Inner:inner, StackTrace:ex.StackTrace(nil)}`, fmt.Sprintf("%#v", i2)) 282 it.Equal(`ex.Ex{Class:error(nil), Message:"", Inner:; inner, StackTrace:ex.StackTrace(nil)}`, fmt.Sprintf("%#v", i3)) 283 it.HasPrefix(fmt.Sprintf("%#v", i4), `ex.Ex{Class:error(nil), Message:"", Inner:(*errors.errorString)(0x`) 284 it.HasSuffix(fmt.Sprintf("%#v", i4), `), StackTrace:ex.StackTrace(nil)}`) 285 it.Equal(`ex.Ex{Class:error(nil), Message:"", Inner:error(nil), StackTrace:[]string{"stack"}}`, fmt.Sprintf("%#v", s)) 286 it.Equal(`ex.Ex{Class:"cls", Message:"msg", Inner:error(nil), StackTrace:ex.StackTrace(nil)}`, fmt.Sprintf("%#v", mc1)) 287 it.HasPrefix(fmt.Sprintf("%#v", mc2), `ex.Ex{Class:(*errors.errorString)(0x`) 288 it.HasSuffix(fmt.Sprintf("%#v", mc2), `), Message:"msg", Inner:error(nil), StackTrace:ex.StackTrace(nil)}`) 289 it.Equal(`ex.Ex{Class:"cls", Message:"msg", Inner:error(nil), StackTrace:[]string{"stack"}}`, fmt.Sprintf("%#v", msc1)) 290 it.HasPrefix(fmt.Sprintf("%#v", msc2), `ex.Ex{Class:(*errors.errorString)(0x`) 291 it.HasSuffix(fmt.Sprintf("%#v", msc2), `), Message:"msg", Inner:error(nil), StackTrace:[]string{"stack"}}`) 292 293 // Format: *Ex -> #v 294 it.Equal("", fmt.Sprintf("%#v", &e)) 295 it.Equal("cls", fmt.Sprintf("%#v", &c1)) 296 it.Equal("cls", fmt.Sprintf("%#v", &c2)) 297 it.Equal("; msg", fmt.Sprintf("%#v", &m)) 298 it.Equal("\ninner", fmt.Sprintf("%#v", &i1)) 299 it.Equal("\ninner", fmt.Sprintf("%#v", &i2)) 300 it.Equal("\n; inner", fmt.Sprintf("%#v", &i3)) 301 it.Equal("\ninner", fmt.Sprintf("%#v", &i4)) 302 it.Equal("", fmt.Sprintf("%#v", &s)) 303 it.Equal("cls; msg", fmt.Sprintf("%#v", &mc1)) 304 it.Equal("cls; msg", fmt.Sprintf("%#v", &mc2)) 305 it.Equal("cls; msg", fmt.Sprintf("%#v", &msc1)) 306 it.Equal("cls; msg", fmt.Sprintf("%#v", &msc2)) 307 308 // Format: Ex -> c 309 // NOTE: trick the linter 310 var cfmt = "%" 311 cfmt += "c" 312 it.Equal("<nil>", fmt.Sprintf(cfmt, n)) 313 it.Equal("{<nil> %!c(string=) <nil> <nil>}", fmt.Sprintf(cfmt, e)) 314 it.Equal("{%!c(ex.Class=cls) %!c(string=) <nil> <nil>}", fmt.Sprintf(cfmt, c1)) 315 it.Equal("{%!c(*errors.errorString=&{cls}) %!c(string=) <nil> <nil>}", fmt.Sprintf(cfmt, c2)) 316 it.Equal("{<nil> %!c(string=msg) <nil> <nil>}", fmt.Sprintf(cfmt, m)) 317 it.Equal("{<nil> %!c(string=) %!c(ex.Class=inner) <nil>}", fmt.Sprintf(cfmt, i1)) 318 it.Equal("{<nil> %!c(string=) inner <nil>}", fmt.Sprintf(cfmt, i2)) 319 it.Equal("{<nil> %!c(string=) %!c(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference) <nil>}", fmt.Sprintf(cfmt, i3)) 320 it.Equal("{<nil> %!c(string=) %!c(*errors.errorString=&{inner}) <nil>}", fmt.Sprintf(cfmt, i4)) 321 it.Equal("{<nil> %!c(string=) <nil> }", fmt.Sprintf(cfmt, s)) 322 it.Equal("{%!c(ex.Class=cls) %!c(string=msg) <nil> <nil>}", fmt.Sprintf(cfmt, mc1)) 323 it.Equal("{%!c(*errors.errorString=&{cls}) %!c(string=msg) <nil> <nil>}", fmt.Sprintf(cfmt, mc2)) 324 it.Equal("{%!c(ex.Class=cls) %!c(string=msg) <nil> }", fmt.Sprintf(cfmt, msc1)) 325 it.Equal("{%!c(*errors.errorString=&{cls}) %!c(string=msg) <nil> }", fmt.Sprintf(cfmt, msc2)) 326 327 // Format: *Ex -> c 328 it.Equal("%!c(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)", fmt.Sprintf("%c", &e)) 329 it.Equal("cls", fmt.Sprintf("%c", &c1)) 330 it.Equal("cls", fmt.Sprintf("%c", &c2)) 331 it.Equal("%!c(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)", fmt.Sprintf("%c", &m)) 332 it.Equal("%!c(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)", fmt.Sprintf("%c", &i1)) 333 it.Equal("%!c(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)", fmt.Sprintf("%c", &i2)) 334 it.Equal("%!c(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)", fmt.Sprintf("%c", &i3)) 335 it.Equal("%!c(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)", fmt.Sprintf("%c", &i4)) 336 it.Equal("%!c(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)", fmt.Sprintf("%c", &s)) 337 it.Equal("cls", fmt.Sprintf("%c", &mc1)) 338 it.Equal("cls", fmt.Sprintf("%c", &mc2)) 339 it.Equal("cls", fmt.Sprintf("%c", &msc1)) 340 it.Equal("cls", fmt.Sprintf("%c", &msc2)) 341 342 // Format: Ex -> i 343 it.Equal("<nil>", fmt.Sprintf("%i", any(n))) 344 it.Equal("{<nil> %!i(string=) <nil> <nil>}", fmt.Sprintf("%i", any(e))) 345 it.Equal("{%!i(ex.Class=cls) %!i(string=) <nil> <nil>}", fmt.Sprintf("%i", any(c1))) 346 it.Equal("{%!i(*errors.errorString=&{cls}) %!i(string=) <nil> <nil>}", fmt.Sprintf("%i", any(c2))) 347 it.Equal("{<nil> %!i(string=msg) <nil> <nil>}", fmt.Sprintf("%i", any(m))) 348 it.Equal("{<nil> %!i(string=) %!i(ex.Class=inner) <nil>}", fmt.Sprintf("%i", any(i1))) 349 it.Equal("{<nil> %!i(string=) <nil>}", fmt.Sprintf("%i", any(i2))) 350 it.Equal("{<nil> %!i(string=) <nil>}", fmt.Sprintf("%i", any(i3))) 351 it.Equal("{<nil> %!i(string=) %!i(*errors.errorString=&{inner}) <nil>}", fmt.Sprintf("%i", any(i4))) 352 it.Equal("{<nil> %!i(string=) <nil> }", fmt.Sprintf("%i", any(s))) 353 it.Equal("{%!i(ex.Class=cls) %!i(string=msg) <nil> <nil>}", fmt.Sprintf("%i", any(mc1))) 354 it.Equal("{%!i(*errors.errorString=&{cls}) %!i(string=msg) <nil> <nil>}", fmt.Sprintf("%i", any(mc2))) 355 it.Equal("{%!i(ex.Class=cls) %!i(string=msg) <nil> }", fmt.Sprintf("%i", any(msc1))) 356 it.Equal("{%!i(*errors.errorString=&{cls}) %!i(string=msg) <nil> }", fmt.Sprintf("%i", any(msc2))) 357 358 // Format: *Ex -> i 359 it.Equal("", fmt.Sprintf("%i", &e)) 360 it.Equal("", fmt.Sprintf("%i", &c1)) 361 it.Equal("", fmt.Sprintf("%i", &c2)) 362 it.Equal("", fmt.Sprintf("%i", &m)) 363 it.Equal("inner", fmt.Sprintf("%i", &i1)) 364 it.Equal("", fmt.Sprintf("%i", &i2)) 365 it.Equal("", fmt.Sprintf("%i", &i3)) 366 it.Equal("inner", fmt.Sprintf("%i", &i4)) 367 it.Equal("", fmt.Sprintf("%i", &s)) 368 it.Equal("", fmt.Sprintf("%i", &mc1)) 369 it.Equal("", fmt.Sprintf("%i", &mc2)) 370 it.Equal("", fmt.Sprintf("%i", &msc1)) 371 it.Equal("", fmt.Sprintf("%i", &msc2)) 372 373 // Format: Ex -> m 374 it.Equal("<nil>", fmt.Sprintf("%m", any(n))) 375 it.Equal("{<nil> %!m(string=) <nil> <nil>}", fmt.Sprintf("%m", any(e))) 376 it.Equal("{%!m(ex.Class=cls) %!m(string=) <nil> <nil>}", fmt.Sprintf("%m", any(c1))) 377 it.Equal("{%!m(*errors.errorString=&{cls}) %!m(string=) <nil> <nil>}", fmt.Sprintf("%m", any(c2))) 378 it.Equal("{<nil> %!m(string=msg) <nil> <nil>}", fmt.Sprintf("%m", any(m))) 379 it.Equal("{<nil> %!m(string=) %!m(ex.Class=inner) <nil>}", fmt.Sprintf("%m", any(i1))) 380 it.Equal("{<nil> %!m(string=) <nil>}", fmt.Sprintf("%m", any(i2))) 381 it.Equal("{<nil> %!m(string=) inner <nil>}", fmt.Sprintf("%m", any(i3))) 382 it.Equal("{<nil> %!m(string=) %!m(*errors.errorString=&{inner}) <nil>}", fmt.Sprintf("%m", any(i4))) 383 it.Equal("{<nil> %!m(string=) <nil> }", fmt.Sprintf("%m", any(s))) 384 it.Equal("{%!m(ex.Class=cls) %!m(string=msg) <nil> <nil>}", fmt.Sprintf("%m", any(mc1))) 385 it.Equal("{%!m(*errors.errorString=&{cls}) %!m(string=msg) <nil> <nil>}", fmt.Sprintf("%m", any(mc2))) 386 it.Equal("{%!m(ex.Class=cls) %!m(string=msg) <nil> }", fmt.Sprintf("%m", any(msc1))) 387 it.Equal("{%!m(*errors.errorString=&{cls}) %!m(string=msg) <nil> }", fmt.Sprintf("%m", any(msc2))) 388 389 // Format: *Ex -> m 390 it.Equal("", fmt.Sprintf("%m", &e)) 391 it.Equal("", fmt.Sprintf("%m", &c1)) 392 it.Equal("", fmt.Sprintf("%m", &c2)) 393 it.Equal("msg", fmt.Sprintf("%m", &m)) 394 it.Equal("", fmt.Sprintf("%m", &i1)) 395 it.Equal("", fmt.Sprintf("%m", &i2)) 396 it.Equal("", fmt.Sprintf("%m", &i3)) 397 it.Equal("", fmt.Sprintf("%m", &i4)) 398 it.Equal("", fmt.Sprintf("%m", &s)) 399 it.Equal("msg", fmt.Sprintf("%m", &mc1)) 400 it.Equal("msg", fmt.Sprintf("%m", &mc2)) 401 it.Equal("msg", fmt.Sprintf("%m", &msc1)) 402 it.Equal("msg", fmt.Sprintf("%m", &msc2)) 403 404 // Format: Ex -> q 405 it.Equal("<nil>", fmt.Sprintf("%q", n)) 406 it.Equal(`{<nil> "" <nil> <nil>}`, fmt.Sprintf("%q", e)) 407 it.Equal(`{"cls" "" <nil> <nil>}`, fmt.Sprintf("%q", c1)) 408 it.Equal(`{"cls" "" <nil> <nil>}`, fmt.Sprintf("%q", c2)) 409 it.Equal(`{<nil> "msg" <nil> <nil>}`, fmt.Sprintf("%q", m)) 410 it.Equal(`{<nil> "" "inner" <nil>}`, fmt.Sprintf("%q", i1)) 411 it.Equal(`{<nil> "" "" <nil>}`, fmt.Sprintf("%q", i2)) 412 it.Equal(`{<nil> "" "inner" <nil>}`, fmt.Sprintf("%q", i3)) 413 it.Equal(`{<nil> "" "inner" <nil>}`, fmt.Sprintf("%q", i4)) 414 it.Equal(`{<nil> "" <nil> }`, fmt.Sprintf("%q", s)) 415 it.Equal(`{"cls" "msg" <nil> <nil>}`, fmt.Sprintf("%q", mc1)) 416 it.Equal(`{"cls" "msg" <nil> <nil>}`, fmt.Sprintf("%q", mc2)) 417 it.Equal(`{"cls" "msg" <nil> }`, fmt.Sprintf("%q", msc1)) 418 it.Equal(`{"cls" "msg" <nil> }`, fmt.Sprintf("%q", msc2)) 419 420 // Format: *Ex -> q 421 it.Equal(`""`, fmt.Sprintf("%q", &e)) 422 it.Equal(`""`, fmt.Sprintf("%q", &c1)) 423 it.Equal(`""`, fmt.Sprintf("%q", &c2)) 424 it.Equal(`"msg"`, fmt.Sprintf("%q", &m)) 425 it.Equal(`""`, fmt.Sprintf("%q", &i1)) 426 it.Equal(`""`, fmt.Sprintf("%q", &i2)) 427 it.Equal(`""`, fmt.Sprintf("%q", &i3)) 428 it.Equal(`""`, fmt.Sprintf("%q", &i4)) 429 it.Equal(`""`, fmt.Sprintf("%q", &s)) 430 it.Equal(`"msg"`, fmt.Sprintf("%q", &mc1)) 431 it.Equal(`"msg"`, fmt.Sprintf("%q", &mc2)) 432 it.Equal(`"msg"`, fmt.Sprintf("%q", &msc1)) 433 it.Equal(`"msg"`, fmt.Sprintf("%q", &msc2)) 434 435 // Format: Ex -> t 436 // NOTE: trick the linter 437 var tfmt = "%" 438 tfmt += "t" 439 it.Equal("", fmt.Sprintf(tfmt, n)) 440 it.Equal("{<nil> %!t(string=) <nil> <nil>}", fmt.Sprintf(tfmt, any(e))) 441 it.Equal("{%!t(ex.Class=cls) %!t(string=) <nil> <nil>}", fmt.Sprintf(tfmt, any(c1))) 442 it.Equal("{%!t(*errors.errorString=&{cls}) %!t(string=) <nil> <nil>}", fmt.Sprintf(tfmt, any(c2))) 443 it.Equal("{<nil> %!t(string=msg) <nil> <nil>}", fmt.Sprintf(tfmt, any(m))) 444 it.Equal("{<nil> %!t(string=) %!t(ex.Class=inner) <nil>}", fmt.Sprintf(tfmt, any(i1))) 445 it.Equal("{<nil> %!t(string=) <nil>}", fmt.Sprintf(tfmt, any(i2))) 446 it.Equal("{<nil> %!t(string=) <nil>}", fmt.Sprintf(tfmt, any(i3))) 447 it.Equal("{<nil> %!t(string=) %!t(*errors.errorString=&{inner}) <nil>}", fmt.Sprintf(tfmt, any(i4))) 448 it.Equal("{<nil> %!t(string=) <nil> }", fmt.Sprintf(tfmt, any(s))) 449 it.Equal("{%!t(ex.Class=cls) %!t(string=msg) <nil> <nil>}", fmt.Sprintf(tfmt, any(mc1))) 450 it.Equal("{%!t(*errors.errorString=&{cls}) %!t(string=msg) <nil> <nil>}", fmt.Sprintf(tfmt, any(mc2))) 451 it.Equal("{%!t(ex.Class=cls) %!t(string=msg) <nil> }", fmt.Sprintf(tfmt, any(msc1))) 452 it.Equal("{%!t(*errors.errorString=&{cls}) %!t(string=msg) <nil> }", fmt.Sprintf(tfmt, any(msc2))) 453 454 // Format: *Ex -> t 455 it.Equal("", fmt.Sprintf("%t", &e)) 456 it.Equal("", fmt.Sprintf("%t", &c1)) 457 it.Equal("", fmt.Sprintf("%t", &c2)) 458 it.Equal("", fmt.Sprintf("%t", &m)) 459 it.Equal("", fmt.Sprintf("%t", &i1)) 460 it.Equal("", fmt.Sprintf("%t", &i2)) 461 it.Equal("", fmt.Sprintf("%t", &i3)) 462 it.Equal("", fmt.Sprintf("%t", &i4)) 463 it.Equal("", fmt.Sprintf("%t", &s)) 464 it.Equal("", fmt.Sprintf("%t", &mc1)) 465 it.Equal("", fmt.Sprintf("%t", &mc2)) 466 it.Equal("", fmt.Sprintf("%t", &msc1)) 467 it.Equal("", fmt.Sprintf("%t", &msc2)) 468 469 // Format: Ex -> [default Sprint] 470 it.Equal("<nil>", fmt.Sprint(n)) 471 it.Equal("{<nil> <nil> <nil>}", fmt.Sprint(e)) 472 it.Equal("{cls <nil> <nil>}", fmt.Sprint(c1)) 473 it.Equal("{cls <nil> <nil>}", fmt.Sprint(c2)) 474 it.Equal("{<nil> msg <nil> <nil>}", fmt.Sprint(m)) 475 it.Equal("{<nil> inner <nil>}", fmt.Sprint(i1)) 476 it.Equal("{<nil> inner <nil>}", fmt.Sprint(i2)) 477 it.Equal("{<nil> ; inner <nil>}", fmt.Sprint(i3)) 478 it.Equal("{<nil> inner <nil>}", fmt.Sprint(i4)) 479 it.Equal("{<nil> <nil> \nstack}", fmt.Sprint(s)) 480 it.Equal("{cls msg <nil> <nil>}", fmt.Sprint(mc1)) 481 it.Equal("{cls msg <nil> <nil>}", fmt.Sprint(mc2)) 482 it.Equal("{cls msg <nil> \nstack}", fmt.Sprint(msc1)) 483 it.Equal("{cls msg <nil> \nstack}", fmt.Sprint(msc2)) 484 485 // Format: *Ex -> [default Sprint] 486 it.Equal("", fmt.Sprint(&e)) 487 it.Equal("cls", fmt.Sprint(&c1)) 488 it.Equal("cls", fmt.Sprint(&c2)) 489 it.Equal("; msg", fmt.Sprint(&m)) 490 it.Equal("\ninner", fmt.Sprint(&i1)) 491 it.Equal("\ninner", fmt.Sprint(&i2)) 492 it.Equal("\n; inner", fmt.Sprint(&i3)) 493 it.Equal("\ninner", fmt.Sprint(&i4)) 494 it.Equal("", fmt.Sprint(&s)) 495 it.Equal("cls; msg", fmt.Sprint(&mc1)) 496 it.Equal("cls; msg", fmt.Sprint(&mc2)) 497 it.Equal("cls; msg", fmt.Sprint(&msc1)) 498 it.Equal("cls; msg", fmt.Sprint(&msc2)) 499 } 500 501 func TestMarshalJSON(t *testing.T) { 502 503 type ReadableStackTrace struct { 504 Class string `json:"Class"` 505 Message string `json:"Message"` 506 Inner error `json:"Inner"` 507 Stack []string `json:"StackTrace"` 508 } 509 510 a := assert.New(t) 511 message := "new test error" 512 ex := As(New(message)) 513 a.NotNil(ex) 514 stackTrace := ex.StackTrace 515 typed, isTyped := stackTrace.(StackPointers) 516 a.True(isTyped) 517 a.NotNil(typed) 518 stackDepth := len(typed) 519 520 jsonErr, err := json.Marshal(ex) 521 a.Nil(err) 522 a.NotNil(jsonErr) 523 524 ex2 := &ReadableStackTrace{} 525 err = json.Unmarshal(jsonErr, ex2) 526 a.Nil(err) 527 a.Len(ex2.Stack, stackDepth) 528 a.Equal(message, ex2.Class) 529 530 ex = As(New(fmt.Errorf(message))) 531 a.NotNil(ex) 532 stackTrace = ex.StackTrace 533 typed, isTyped = stackTrace.(StackPointers) 534 a.True(isTyped) 535 a.NotNil(typed) 536 stackDepth = len(typed) 537 538 jsonErr, err = json.Marshal(ex) 539 a.Nil(err) 540 a.NotNil(jsonErr) 541 542 ex2 = &ReadableStackTrace{} 543 err = json.Unmarshal(jsonErr, ex2) 544 a.Nil(err) 545 a.Len(ex2.Stack, stackDepth) 546 a.Equal(message, ex2.Class) 547 } 548 549 func TestJSON(t *testing.T) { 550 assert := assert.New(t) 551 552 ex := New("this is a test", 553 OptMessage("test message"), 554 OptInner(New("inner exception", OptMessagef("inner test message"))), 555 ) 556 557 contents, err := json.Marshal(ex) 558 assert.Nil(err) 559 560 var verify Ex 561 err = json.Unmarshal(contents, &verify) 562 assert.Nil(err) 563 564 assert.Equal(ErrClass(ex), ErrClass(verify)) 565 assert.Equal(ErrMessage(ex), ErrMessage(verify)) 566 assert.NotNil(verify.Inner) 567 assert.Equal(ErrClass(ErrInner(ex)), ErrClass(ErrInner(verify))) 568 assert.Equal(ErrMessage(ErrInner(ex)), ErrMessage(ErrInner(verify))) 569 } 570 571 func TestNest(t *testing.T) { 572 a := assert.New(t) 573 574 ex1 := As(New("this is an error")) 575 ex2 := As(New("this is another error")) 576 err := As(Nest(ex1, ex2)) 577 578 a.NotNil(err) 579 a.NotNil(err.Inner) 580 a.NotEmpty(err.Error()) 581 582 a.True(Is(ex1, Class("this is an error"))) 583 a.True(Is(ex1.Inner, Class("this is another error"))) 584 } 585 586 func TestNestNil(t *testing.T) { 587 a := assert.New(t) 588 589 var ex1 error 590 var ex2 error 591 var ex3 error 592 593 err := Nest(ex1, ex2, ex3) 594 a.Nil(err) 595 a.Equal(nil, err) 596 a.True(nil == err) 597 } 598 599 func TestExceptionFormat(t *testing.T) { 600 assert := assert.New(t) 601 602 e := &Ex{Class: fmt.Errorf("this is only a test")} 603 output := fmt.Sprintf("%v", e) 604 assert.Equal("this is only a test", output) 605 606 output = fmt.Sprintf("%+v", e) 607 assert.Equal("this is only a test", output) 608 609 e = &Ex{ 610 Class: fmt.Errorf("this is only a test"), 611 StackTrace: StackStrings([]string{ 612 "foo", 613 "bar", 614 }), 615 } 616 617 output = fmt.Sprintf("%+v", e) 618 assert.Equal("this is only a test\nfoo\nbar", output) 619 } 620 621 func TestExceptionPrintsInner(t *testing.T) { 622 assert := assert.New(t) 623 624 ex := New("outer", OptInner(New("middle", OptInner(New("terminal"))))) 625 626 output := fmt.Sprintf("%v", ex) 627 628 assert.Contains(output, "outer") 629 assert.Contains(output, "middle") 630 assert.Contains(output, "terminal") 631 632 output = fmt.Sprintf("%+v", ex) 633 634 assert.Contains(output, "outer") 635 assert.Contains(output, "middle") 636 assert.Contains(output, "terminal") 637 } 638 639 type structuredError struct { 640 value string 641 } 642 643 func (err structuredError) Error() string { 644 return err.value 645 } 646 647 func TestException_ErrorsIsCompatability(t *testing.T) { 648 assert := assert.New(t) 649 650 { // Single nesting, Ex is outermost 651 innerErr := errors.New("inner") 652 outerErr := New("outer", OptInnerClass(innerErr)) 653 654 assert.True(errors.Is(outerErr, innerErr)) 655 } 656 657 { // Single nesting, Ex is innermost 658 innerErr := New("inner") 659 outerErr := fmt.Errorf("outer: %w", innerErr) 660 661 assert.True(errors.Is(outerErr, Class("inner"))) 662 } 663 664 { // Triple nesting, including Ex and non-Ex 665 firstErr := errors.New("inner most") 666 secondErr := fmt.Errorf("standard err: %w", firstErr) 667 thirdErr := New("ex err", OptInner(secondErr)) 668 fourthErr := New("outer most", OptInner(thirdErr)) 669 670 assert.True(errors.Is(fourthErr, firstErr)) 671 assert.True(errors.Is(fourthErr, secondErr)) 672 assert.True(errors.Is(fourthErr, Class("ex err"))) 673 } 674 675 { // Target is nested in an Ex class and not in Inner chain 676 firstErr := errors.New("inner most") 677 secondErr := fmt.Errorf("standard err: %w", firstErr) 678 thirdErr := New(secondErr, OptInner(fmt.Errorf("another cause"))) 679 680 assert.True(errors.Is(thirdErr, firstErr)) 681 assert.True(errors.Is(thirdErr, secondErr)) 682 } 683 684 { // Simple Ex against a Multi 685 firstErr := New("ex") 686 secondErr := errors.New("second") 687 thirdErr := Multi{firstErr, secondErr} 688 689 assert.False(errors.Is(firstErr, thirdErr)) 690 } 691 692 { // Ex wrapping a Multi testing against Multi 693 firstErr := errors.New("inner most") 694 secondErr := fmt.Errorf("standard err: %w", firstErr) 695 thirdErr := New(Multi{firstErr, secondErr}) 696 697 assert.True(errors.Is(thirdErr, firstErr)) 698 assert.True(errors.Is(thirdErr, secondErr)) 699 assert.True(errors.Is(thirdErr, thirdErr)) 700 assert.False(errors.Is(thirdErr, Multi{firstErr, secondErr})) 701 assert.False(errors.Is(thirdErr, Multi{firstErr, secondErr, secondErr})) 702 assert.False(errors.Is(thirdErr, Multi{secondErr, firstErr})) 703 } 704 705 { 706 // nil checks 707 var nilEx *Ex 708 assert.False(errors.Is(nilEx, nil)) 709 assert.False(errors.Is(&Ex{}, nil)) 710 } 711 } 712 713 func TestException_ErrorsAsCompatability(t *testing.T) { 714 assert := assert.New(t) 715 716 { // Single nesting, targeting non-Ex 717 innerErr := structuredError{"inner most"} 718 outerErr := New("outer", OptInner(innerErr)) 719 720 var matchedErr structuredError 721 assert.True(errors.As(outerErr, &matchedErr)) 722 assert.Equal("inner most", matchedErr.value) 723 } 724 725 { // Single nesting, targeting Ex 726 innerErr := New("outer most") 727 outerErr := fmt.Errorf("outer err: %w", innerErr) 728 729 var matchedErr *Ex 730 assert.True(errors.As(outerErr, &matchedErr)) 731 assert.Equal("outer most", matchedErr.Class.Error()) 732 } 733 734 { // Single nesting, targeting inner Ex class 735 innerErr := New(structuredError{"inner most"}) 736 outerErr := New("outer most", OptInner(innerErr)) 737 738 var matchedErr structuredError 739 assert.True(errors.As(outerErr, &matchedErr)) 740 assert.Equal("inner most", matchedErr.value) 741 } 742 743 { // Triple Nesting, targeting non-Ex 744 firstErr := structuredError{"inner most"} 745 secondErr := fmt.Errorf("standard err: %w", firstErr) 746 thirdErr := New("ex err", OptInner(secondErr)) 747 fourthErr := New("outer most", OptInner(thirdErr)) 748 749 var matchedErr structuredError 750 assert.True(errors.As(fourthErr, &matchedErr)) 751 assert.Equal("inner most", matchedErr.value) 752 } 753 }