package exerr import ( "encoding/json" "errors" "fmt" "strings" "testing" "time" "git.blackforestbytes.com/BlackForestBytes/goext/tst" "go.mongodb.org/mongo-driver/v2/bson" ) // ============================================================================ // Builder / Constructor tests // ============================================================================ func TestNewBuildsExErr(t *testing.T) { err := New(TypeInternal, "boom").Build() tst.AssertTrue(t, err != nil) ee, ok := err.(*ExErr) tst.AssertTrue(t, ok) tst.AssertEqual(t, ee.Message, "boom") tst.AssertEqual(t, ee.Type, TypeInternal) tst.AssertEqual(t, ee.Category, CatSystem) tst.AssertEqual(t, ee.Severity, SevErr) tst.AssertTrue(t, ee.UniqueID != "") } func TestWrapNilProducesInternalError(t *testing.T) { err := Wrap(nil, "msg").Build() tst.AssertTrue(t, err != nil) ee, _ := err.(*ExErr) tst.AssertEqual(t, ee.Message, "msg") tst.AssertEqual(t, ee.Type, TypeInternal) } func TestWrapForeignError(t *testing.T) { plain := errors.New("plain go error") err := Wrap(plain, "wrapped").Build() ee, _ := err.(*ExErr) // outer is a wrap (TypeWrap), inner is the foreign error tst.AssertEqual(t, ee.Type, TypeWrap) tst.AssertEqual(t, ee.Category, CatWrap) tst.AssertEqual(t, ee.Message, "wrapped") tst.AssertTrue(t, ee.OriginalError != nil) tst.AssertEqual(t, ee.OriginalError.Category, CatForeign) tst.AssertEqual(t, ee.OriginalError.Message, "plain go error") } func TestWrapExErrChainsDepth(t *testing.T) { e1 := New(TypeInternal, "level-1").Build() e2 := Wrap(e1, "level-2").Build() e3 := Wrap(e2, "level-3").Build() ee3 := e3.(*ExErr) tst.AssertEqual(t, ee3.Depth(), 3) } func TestGetReturnsExErr(t *testing.T) { plain := errors.New("foreign") b := Get(plain) tst.AssertTrue(t, b != nil) tst.AssertEqual(t, b.errorData.Category, CatForeign) tst.AssertEqual(t, b.errorData.Message, "foreign") } func TestBuilderWithModifiers(t *testing.T) { err := New(TypeInternal, "msg"). WithType(TypeAssert). WithStatuscode(418). WithMessage("teapot"). WithSeverity(SevWarn). WithCategory(CatUser). Build() ee := err.(*ExErr) tst.AssertEqual(t, ee.Type, TypeAssert) tst.AssertDeRefEqual(t, ee.StatusCode, 418) tst.AssertEqual(t, ee.Message, "teapot") tst.AssertEqual(t, ee.Severity, SevWarn) tst.AssertEqual(t, ee.Category, CatUser) } func TestBuilderSeverityShortcuts(t *testing.T) { tst.AssertEqual(t, New(TypeInternal, "x").Err().Build().(*ExErr).Severity, SevErr) tst.AssertEqual(t, New(TypeInternal, "x").Warn().Build().(*ExErr).Severity, SevWarn) tst.AssertEqual(t, New(TypeInternal, "x").Info().Build().(*ExErr).Severity, SevInfo) } func TestBuilderCategoryShortcuts(t *testing.T) { tst.AssertEqual(t, New(TypeInternal, "x").User().Build().(*ExErr).Category, CatUser) tst.AssertEqual(t, New(TypeInternal, "x").System().Build().(*ExErr).Category, CatSystem) } func TestBuilderNoLog(t *testing.T) { b := New(TypeInternal, "x").NoLog() tst.AssertTrue(t, b.noLog) } func TestBuilderExtra(t *testing.T) { err := New(TypeInternal, "x").Extra("k", 42).Build() ee := err.(*ExErr) v, ok := ee.GetExtra("k") tst.AssertTrue(t, ok) tst.AssertEqual(t, v.(int), 42) } func TestBuilderMetaTypes(t *testing.T) { now := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC) err := New(TypeInternal, "msg"). Str("s", "value"). Int("i", 7). Int8("i8", 8). Int16("i16", 16). Int32("i32", 32). Int64("i64", 64). Float32("f32", 1.5). Float64("f64", 2.5). Bool("b", true). Bytes("by", []byte{0xAA, 0xBB}). Time("t", now). Dur("d", 5*time.Second). Strs("strs", []string{"a", "b"}). Ints("ints", []int{1, 2, 3}). Ints32("ints32", []int32{4, 5}). Type("typ", "hello"). Build() ee := err.(*ExErr) gotS, _ := ee.GetMetaString("s") tst.AssertEqual(t, gotS, "value") gotI, _ := ee.GetMetaInt("i") tst.AssertEqual(t, gotI, 7) gotB, _ := ee.GetMetaBool("b") tst.AssertEqual(t, gotB, true) gotF32, _ := ee.GetMetaFloat32("f32") tst.AssertEqual(t, gotF32, float32(1.5)) gotF64, _ := ee.GetMetaFloat64("f64") tst.AssertEqual(t, gotF64, 2.5) gotT, _ := ee.GetMetaTime("t") tst.AssertEqual(t, gotT.Equal(now), true) } func TestBuilderInterfaceAndAny(t *testing.T) { type payload struct { A int `json:"a"` B string `json:"b"` } err := New(TypeInternal, "msg").Interface("p", payload{A: 1, B: "x"}).Any("p2", payload{A: 2, B: "y"}).Build() ee := err.(*ExErr) v1, ok := ee.GetMeta("p") tst.AssertTrue(t, ok) mv := v1.(AnyWrap) tst.AssertTrue(t, strings.Contains(mv.Json, "\"a\":1")) _, ok = ee.GetMeta("p2") tst.AssertTrue(t, ok) } func TestBuilderStack(t *testing.T) { err := New(TypeInternal, "msg").Stack().Build() ee := err.(*ExErr) v, ok := ee.GetMetaString("@Stack") tst.AssertTrue(t, ok) tst.AssertTrue(t, len(v) > 0) } func TestBuilderErrs(t *testing.T) { in := []error{errors.New("first"), errors.New("second")} err := New(TypeInternal, "msg").Errs("errs", in).Build() ee := err.(*ExErr) v0, ok := ee.GetMetaString("errs[0]") tst.AssertTrue(t, ok) tst.AssertTrue(t, strings.Contains(v0, "first")) v1, ok := ee.GetMetaString("errs[1]") tst.AssertTrue(t, ok) tst.AssertTrue(t, strings.Contains(v1, "second")) } type stringerImpl struct{ s string } func (s stringerImpl) String() string { return s.s } func TestBuilderStringerAndId(t *testing.T) { err := New(TypeInternal, "msg"). Stringer("s", stringerImpl{s: "hello"}). Id("id", stringerImpl{s: "abc-123"}). Build() ee := err.(*ExErr) v, _ := ee.GetMetaString("s") tst.AssertEqual(t, v, "hello") idv, ok := ee.GetMeta("id") tst.AssertTrue(t, ok) w := idv.(IDWrap) tst.AssertEqual(t, w.Value, "abc-123") } func TestBuilderObjectID(t *testing.T) { oid := bson.NewObjectID() err := New(TypeInternal, "msg").ObjectID("oid", oid).Build() ee := err.(*ExErr) mv, ok := ee.Meta["oid"] tst.AssertTrue(t, ok) tst.AssertEqual(t, mv.DataType, MDTObjectID) tst.AssertEqual(t, mv.Value.(bson.ObjectID), oid) } func TestBuilderStrPtr(t *testing.T) { s := "hello" err := New(TypeInternal, "msg").StrPtr("p", &s).StrPtr("n", nil).Build() ee := err.(*ExErr) _, ok := ee.Meta["p"] tst.AssertTrue(t, ok) _, ok = ee.Meta["n"] tst.AssertTrue(t, ok) } func TestBuilderMetaCollision(t *testing.T) { err := New(TypeInternal, "msg").Str("k", "v1").Str("k", "v2").Str("k", "v3").Build() ee := err.(*ExErr) v1, _ := ee.GetMetaString("k") tst.AssertEqual(t, v1, "v1") v2, ok := ee.GetMetaString("k-2") tst.AssertTrue(t, ok) tst.AssertEqual(t, v2, "v2") v3, ok := ee.GetMetaString("k-3") tst.AssertTrue(t, ok) tst.AssertEqual(t, v3, "v3") } // ============================================================================ // FromError tests // ============================================================================ func TestFromErrorNil(t *testing.T) { ee := FromError(nil) tst.AssertTrue(t, ee != nil) tst.AssertEqual(t, ee.Category, CatForeign) tst.AssertEqual(t, ee.WrappedErrType, "nil") } func TestFromErrorPassThrough(t *testing.T) { orig := New(TypeInternal, "msg").Build().(*ExErr) got := FromError(orig) tst.AssertTrue(t, orig == got) } func TestFromErrorForeign(t *testing.T) { in := errors.New("standard") ee := FromError(in) tst.AssertEqual(t, ee.Category, CatForeign) tst.AssertEqual(t, ee.Message, "standard") } // ============================================================================ // ExErr method tests // ============================================================================ func TestErrorReturnsRecursiveMessage(t *testing.T) { in := errors.New("orig") err := Wrap(in, "outer").Build() tst.AssertTrue(t, strings.Contains(err.Error(), "outer") || strings.Contains(err.Error(), "orig")) } func TestUnwrapReturnsOriginalError(t *testing.T) { e1 := New(TypeInternal, "inner").Build().(*ExErr) e2 := Wrap(e1, "outer").Build().(*ExErr) tst.AssertEqual(t, e2.Unwrap().(*ExErr) == e1, true) } func TestUnwrapForeign(t *testing.T) { in := errors.New("std") ee := FromError(in) u := ee.Unwrap() tst.AssertEqual(t, u.Error(), "std") } func TestUnwrapNilWhenNoneAvailable(t *testing.T) { ee := New(TypeInternal, "msg").Build().(*ExErr) tst.AssertTrue(t, ee.Unwrap() == nil) } func TestRecursiveMessageSkipsForeign(t *testing.T) { in := errors.New("foreign-msg") err := Wrap(in, "wrapped-msg").Build().(*ExErr) tst.AssertEqual(t, err.RecursiveMessage(), "wrapped-msg") } func TestRecursiveMessageFallback(t *testing.T) { ee := &ExErr{Message: "self"} tst.AssertEqual(t, ee.RecursiveMessage(), "self") } func TestRecursiveType(t *testing.T) { e1 := New(TypeAssert, "inner").Build().(*ExErr) e2 := Wrap(e1, "outer").Build().(*ExErr) tst.AssertEqual(t, e2.RecursiveType(), TypeAssert) } func TestRecursiveStatuscode(t *testing.T) { e1 := New(TypeInternal, "inner").WithStatuscode(404).Build().(*ExErr) e2 := Wrap(e1, "outer").Build().(*ExErr) got := e2.RecursiveStatuscode() tst.AssertDeRefEqual(t, got, 404) } func TestRecursiveStatuscodeNil(t *testing.T) { e1 := New(TypeWrap, "x").Build().(*ExErr) tst.AssertTrue(t, e1.RecursiveStatuscode() == nil) } func TestRecursiveCategory(t *testing.T) { e1 := New(TypeInternal, "inner").User().Build().(*ExErr) e2 := Wrap(e1, "outer").Build().(*ExErr) tst.AssertEqual(t, e2.RecursiveCategory(), CatUser) } func TestRecursiveMeta(t *testing.T) { e1 := New(TypeInternal, "inner").Str("xkey", "xval").Build().(*ExErr) e2 := Wrap(e1, "outer").Build().(*ExErr) mv := e2.RecursiveMeta("xkey") tst.AssertTrue(t, mv != nil) tst.AssertEqual(t, mv.Value.(string), "xval") tst.AssertTrue(t, e2.RecursiveMeta("nope") == nil) } func TestDepth(t *testing.T) { e1 := New(TypeInternal, "1").Build().(*ExErr) tst.AssertEqual(t, e1.Depth(), 1) e2 := Wrap(e1, "2").Build().(*ExErr) tst.AssertEqual(t, e2.Depth(), 2) e3 := Wrap(e2, "3").Build().(*ExErr) tst.AssertEqual(t, e3.Depth(), 3) } func TestGetMetaStringTypeMismatch(t *testing.T) { err := New(TypeInternal, "msg").Int("k", 1).Build().(*ExErr) _, ok := err.GetMetaString("k") tst.AssertFalse(t, ok) } func TestGetMetaMissing(t *testing.T) { err := New(TypeInternal, "msg").Build().(*ExErr) _, ok := err.GetMeta("missing") tst.AssertFalse(t, ok) } func TestGetExtraMissing(t *testing.T) { err := New(TypeInternal, "msg").Build().(*ExErr) _, ok := err.GetExtra("missing") tst.AssertFalse(t, ok) } func TestUniqueIDsCollects(t *testing.T) { e1 := New(TypeInternal, "1").Build().(*ExErr) e2 := Wrap(e1, "2").Build().(*ExErr) e3 := Wrap(e2, "3").Build().(*ExErr) ids := e3.UniqueIDs() tst.AssertEqual(t, len(ids), 3) } // ============================================================================ // Format / Log // ============================================================================ func TestFormatLogShort(t *testing.T) { err := New(TypeAssert, "boom").Build().(*ExErr) out := err.FormatLog(LogPrintShort) tst.AssertTrue(t, strings.Contains(out, "boom")) tst.AssertTrue(t, strings.Contains(out, TypeAssert.Key)) } func TestFormatLogOverview(t *testing.T) { e1 := New(TypeAssert, "inner").Build().(*ExErr) e2 := Wrap(e1, "outer").Build().(*ExErr) out := e2.FormatLog(LogPrintOverview) tst.AssertTrue(t, strings.Contains(out, "outer")) tst.AssertTrue(t, strings.Contains(out, "inner")) } func TestFormatLogFull(t *testing.T) { err := New(TypeInternal, "boom").Str("k", "v").Build().(*ExErr) out := err.FormatLog(LogPrintFull) tst.AssertTrue(t, strings.Contains(out, "boom")) tst.AssertTrue(t, strings.Contains(out, "k")) } func TestFormatLogUnknownLevel(t *testing.T) { err := New(TypeInternal, "boom").Build().(*ExErr) out := err.FormatLog(LogPrintLevel("__nope__")) tst.AssertTrue(t, strings.HasPrefix(out, "[?[")) } func TestBuilderFormat(t *testing.T) { b := New(TypeInternal, "boom") out := b.Format(LogPrintShort) tst.AssertTrue(t, strings.Contains(out, "boom")) } // ============================================================================ // helper.go // ============================================================================ func TestIsTypeMatching(t *testing.T) { err := New(TypeAssert, "x").Build() tst.AssertTrue(t, IsType(err, TypeAssert)) tst.AssertFalse(t, IsType(err, TypeNotImplemented)) tst.AssertFalse(t, IsType(nil, TypeAssert)) } func TestIsTypeRecursive(t *testing.T) { e1 := New(TypeAssert, "inner").Build() e2 := Wrap(e1, "outer").Build() tst.AssertTrue(t, IsType(e2, TypeAssert)) } func TestIsFromIdentity(t *testing.T) { e := errors.New("x") tst.AssertTrue(t, IsFrom(e, e)) tst.AssertFalse(t, IsFrom(nil, e)) } func TestIsFromForeign(t *testing.T) { src := errors.New("origmsg") wrap := Wrap(src, "outer").Build() tst.AssertTrue(t, IsFrom(wrap, src)) other := errors.New("other") tst.AssertFalse(t, IsFrom(wrap, other)) } func TestHasSourceMessage(t *testing.T) { src := errors.New("origmsg") wrap := Wrap(src, "outer").Build() tst.AssertTrue(t, HasSourceMessage(wrap, "origmsg")) tst.AssertFalse(t, HasSourceMessage(wrap, "other")) tst.AssertFalse(t, HasSourceMessage(nil, "x")) } func TestMessageMatch(t *testing.T) { e := New(TypeInternal, "alpha-beta").Build() tst.AssertTrue(t, MessageMatch(e, func(s string) bool { return strings.Contains(s, "alpha") })) tst.AssertFalse(t, MessageMatch(e, func(s string) bool { return strings.Contains(s, "missing") })) tst.AssertFalse(t, MessageMatch(nil, func(s string) bool { return true })) } func TestOriginalError(t *testing.T) { src := errors.New("the-source") wrap := Wrap(src, "outer").Build() got := OriginalError(wrap) tst.AssertEqual(t, got.Error(), "the-source") tst.AssertTrue(t, OriginalError(nil) == nil) } func TestUniqueIDHelper(t *testing.T) { err := New(TypeInternal, "x").Build() id := UniqueID(err) tst.AssertTrue(t, id != nil) tst.AssertTrue(t, *id != "") tst.AssertTrue(t, UniqueID(nil) == nil) plain := errors.New("plain") tst.AssertTrue(t, UniqueID(plain) == nil) } // ============================================================================ // errors.Is / errors.As // ============================================================================ type customErr struct{ msg string } func (c customErr) Error() string { return c.msg } func TestErrorsAsForeign(t *testing.T) { src := customErr{msg: "x"} wrap := Wrap(src, "outer").Build() var got customErr ok := errors.As(wrap, &got) tst.AssertTrue(t, ok) tst.AssertEqual(t, got.msg, "x") } func TestErrorsIsForeign(t *testing.T) { src := customErr{msg: "x"} wrap := Wrap(src, "outer").Build() tst.AssertTrue(t, errors.Is(wrap, customErr{msg: "x"})) tst.AssertFalse(t, errors.Is(wrap, customErr{msg: "y"})) } // ============================================================================ // MetaValue serialize / deserialize roundtrip // ============================================================================ func TestMetaValueRoundtripPrimitives(t *testing.T) { cases := []MetaValue{ {DataType: MDTString, Value: "hello"}, {DataType: MDTInt, Value: 42}, {DataType: MDTInt8, Value: int8(8)}, {DataType: MDTInt16, Value: int16(16)}, {DataType: MDTInt32, Value: int32(32)}, {DataType: MDTInt64, Value: int64(64)}, {DataType: MDTFloat32, Value: float32(1.5)}, {DataType: MDTFloat64, Value: float64(2.5)}, {DataType: MDTBool, Value: true}, {DataType: MDTBool, Value: false}, {DataType: MDTBytes, Value: []byte{0x01, 0x02, 0xAB}}, {DataType: MDTStringArray, Value: []string{"a", "b"}}, {DataType: MDTIntArray, Value: []int{1, 2, 3}}, {DataType: MDTInt32Array, Value: []int32{4, 5}}, {DataType: MDTNil, Value: nil}, } for _, c := range cases { s, err := c.SerializeValue() tst.AssertNoErr(t, err) var dec MetaValue tst.AssertNoErr(t, dec.Deserialize(s, c.DataType)) tst.AssertStrRepEqual(t, dec.Value, c.Value) } } func TestMetaValueRoundtripStringPtr(t *testing.T) { v := "hello" mv := MetaValue{DataType: MDTStringPtr, Value: &v} s, err := mv.SerializeValue() tst.AssertNoErr(t, err) var dec MetaValue tst.AssertNoErr(t, dec.Deserialize(s, MDTStringPtr)) tst.AssertEqual(t, *(dec.Value.(*string)), v) mv2 := MetaValue{DataType: MDTStringPtr, Value: (*string)(nil)} s, err = mv2.SerializeValue() tst.AssertNoErr(t, err) tst.AssertEqual(t, s, "#") } func TestMetaValueRoundtripTime(t *testing.T) { tm := time.Date(2025, 4, 27, 12, 34, 56, 12345, time.UTC) mv := MetaValue{DataType: MDTTime, Value: tm} s, err := mv.SerializeValue() tst.AssertNoErr(t, err) var dec MetaValue tst.AssertNoErr(t, dec.Deserialize(s, MDTTime)) got := dec.Value.(time.Time) tst.AssertEqual(t, got.Unix(), tm.Unix()) tst.AssertEqual(t, got.Nanosecond(), tm.Nanosecond()) } func TestMetaValueRoundtripDuration(t *testing.T) { d := 3*time.Second + 250*time.Millisecond mv := MetaValue{DataType: MDTDuration, Value: d} s, err := mv.SerializeValue() tst.AssertNoErr(t, err) var dec MetaValue tst.AssertNoErr(t, dec.Deserialize(s, MDTDuration)) tst.AssertEqual(t, dec.Value.(time.Duration), d) } func TestMetaValueRoundtripObjectID(t *testing.T) { oid := bson.NewObjectID() mv := MetaValue{DataType: MDTObjectID, Value: oid} s, err := mv.SerializeValue() tst.AssertNoErr(t, err) var dec MetaValue tst.AssertNoErr(t, dec.Deserialize(s, MDTObjectID)) tst.AssertEqual(t, dec.Value.(bson.ObjectID), oid) } func TestMetaValueDeserializeUnknownType(t *testing.T) { var mv MetaValue err := mv.Deserialize("x", metaDataType("unknown")) tst.AssertTrue(t, err != nil) } func TestMetaValueDeserializeBadBool(t *testing.T) { var mv MetaValue err := mv.Deserialize("nope", MDTBool) tst.AssertTrue(t, err != nil) } func TestMetaValueShortString(t *testing.T) { cases := []struct { mv MetaValue out string }{ {MetaValue{DataType: MDTString, Value: "hello"}, "hello"}, {MetaValue{DataType: MDTInt, Value: 42}, "42"}, {MetaValue{DataType: MDTBool, Value: true}, "true"}, {MetaValue{DataType: MDTNil, Value: nil}, "<>"}, } for _, c := range cases { tst.AssertEqual(t, c.mv.ShortString(100), c.out) } } func TestMetaValueValueString(t *testing.T) { mv := MetaValue{DataType: MDTInt, Value: 42} tst.AssertEqual(t, mv.ValueString(), "42") mv = MetaValue{DataType: MDTString, Value: "ok"} tst.AssertEqual(t, mv.ValueString(), "ok") } func TestMetaValueJSONMarshal(t *testing.T) { mv := MetaValue{DataType: MDTString, Value: "abc"} bin, err := json.Marshal(mv) tst.AssertNoErr(t, err) var dec MetaValue tst.AssertNoErr(t, json.Unmarshal(bin, &dec)) tst.AssertEqual(t, dec.DataType, MDTString) tst.AssertEqual(t, dec.Value.(string), "abc") } func TestMetaValueJSONMarshalInvalidString(t *testing.T) { var mv MetaValue err := json.Unmarshal([]byte("\"badformat\""), &mv) tst.AssertTrue(t, err != nil) } // ============================================================================ // MetaMap // ============================================================================ func TestMetaMapAny(t *testing.T) { mm := MetaMap{} tst.AssertFalse(t, mm.Any()) mm.add("k", MDTString, "v") tst.AssertTrue(t, mm.Any()) } func TestMetaMapFormatOneLine(t *testing.T) { mm := MetaMap{} mm.add("k", MDTString, "v") out := mm.FormatOneLine(100) tst.AssertTrue(t, strings.Contains(out, "k")) tst.AssertTrue(t, strings.Contains(out, "v")) } func TestMetaMapFormatMultiLine(t *testing.T) { mm := MetaMap{} mm.add("k1", MDTString, "v1") mm.add("gin_body", MDTString, "should-be-skipped") out := mm.FormatMultiLine("", " ", 100) tst.AssertTrue(t, strings.Contains(out, "k1")) tst.AssertFalse(t, strings.Contains(out, "should-be-skipped")) } // ============================================================================ // typeWrapper.go // ============================================================================ func TestIDWrap(t *testing.T) { w := newIDWrap(stringerImpl{s: "id-1"}) tst.AssertEqual(t, w.Value, "id-1") tst.AssertFalse(t, w.IsNil) s := w.Serialize() got := deserializeIDWrap(s) tst.AssertEqual(t, got.Value, "id-1") tst.AssertEqual(t, got.Type, w.Type) } func TestIDWrapNil(t *testing.T) { var nilStringer fmt.Stringer = (*stringerImpl)(nil) w := newIDWrap(nilStringer) tst.AssertTrue(t, w.IsNil) s := w.Serialize() got := deserializeIDWrap(s) tst.AssertTrue(t, got.IsNil) } func TestAnyWrap(t *testing.T) { type p struct { X int `json:"x"` } w := newAnyWrap(p{X: 7}) tst.AssertFalse(t, w.IsError) tst.AssertFalse(t, w.IsNil) tst.AssertTrue(t, strings.Contains(w.Json, "\"x\":7")) s := w.Serialize() got := deserializeAnyWrap(s) tst.AssertEqual(t, got.IsError, false) tst.AssertTrue(t, strings.Contains(got.Json, "\"x\":7")) } func TestAnyWrapNil(t *testing.T) { w := newAnyWrap(nil) tst.AssertTrue(t, w.IsNil) s := w.Serialize() got := deserializeAnyWrap(s) tst.AssertTrue(t, got.IsNil) } func TestAnyWrapDeserializeBad(t *testing.T) { got := deserializeAnyWrap("xx") tst.AssertTrue(t, got.IsError) } // ============================================================================ // dataType.go // ============================================================================ func TestNewTypeRegisters(t *testing.T) { custom := NewType("UNIT_TEST_CUSTOM_TYPE", new(503)) tst.AssertEqual(t, custom.Key, "UNIT_TEST_CUSTOM_TYPE") tst.AssertDeRefEqual(t, custom.DefaultStatusCode, 503) all := ListRegisteredTypes() found := false for _, et := range all { if et.Key == "UNIT_TEST_CUSTOM_TYPE" { found = true break } } tst.AssertTrue(t, found) } func TestErrorTypeJSONUnmarshalKnown(t *testing.T) { var et ErrorType tst.AssertNoErr(t, json.Unmarshal([]byte("\"NOT_IMPLEMENTED\""), &et)) tst.AssertEqual(t, et.Key, TypeNotImplemented.Key) } func TestErrorTypeJSONUnmarshalUnknown(t *testing.T) { var et ErrorType tst.AssertNoErr(t, json.Unmarshal([]byte("\"COMPLETELY_UNKNOWN_TYPE_QQQ\""), &et)) tst.AssertEqual(t, et.Key, "COMPLETELY_UNKNOWN_TYPE_QQQ") tst.AssertTrue(t, et.DefaultStatusCode == nil) } func TestCategoryAndSeverityJSONMarshal(t *testing.T) { bin, err := json.Marshal(CatUser) tst.AssertNoErr(t, err) tst.AssertEqual(t, string(bin), "\"User\"") var c ErrorCategory tst.AssertNoErr(t, json.Unmarshal(bin, &c)) tst.AssertEqual(t, c, CatUser) bin, err = json.Marshal(SevWarn) tst.AssertNoErr(t, err) tst.AssertEqual(t, string(bin), "\"Warn\"") } // ============================================================================ // proxy.go // ============================================================================ func TestProxy(t *testing.T) { ee := New(TypeInternal, "x").Build().(*ExErr) p := Proxy{v: *ee} tst.AssertEqual(t, p.UniqueID(), ee.UniqueID) tst.AssertEqual(t, p.Get().Message, ee.Message) } // ============================================================================ // listener.go // ============================================================================ func TestRegisterListenerInvokedOnBuild(t *testing.T) { var gotMethod Method var gotErr *ExErr RegisterListener(func(method Method, v *ExErr, opt ListenerOpt) { if v != nil && strings.Contains(v.Message, "listener-marker-1") { gotMethod = method gotErr = v } }) _ = New(TypeInternal, "listener-marker-1").Build() tst.AssertEqual(t, gotMethod, MethodBuild) tst.AssertTrue(t, gotErr != nil) } // ============================================================================ // Initialized // ============================================================================ func TestInitialized(t *testing.T) { tst.AssertTrue(t, Initialized()) } // ============================================================================ // JSON output (toJson / ToAPIJson) // ============================================================================ func TestToAPIJsonContainsCoreFields(t *testing.T) { err := New(TypeInternal, "boom").Str("k", "v").Extra("ex", 1).Build().(*ExErr) out := err.ToAPIJson(false, true, true) tst.AssertEqual(t, out["errorid"].(string), err.UniqueID) tst.AssertEqual(t, out["errorcode"].(string), TypeInternal.Key) tst.AssertEqual(t, out["category"].(string), CatSystem.Category) tst.AssertEqual(t, out["message"].(string), "boom") _, hasData := out["__data"] tst.AssertTrue(t, hasData) tst.AssertEqual(t, out["ex"].(int), 1) } func TestToDefaultAPIJson(t *testing.T) { err := New(TypeInternal, "boom").Build().(*ExErr) out, jerr := err.ToDefaultAPIJson() tst.AssertNoErr(t, jerr) tst.AssertTrue(t, strings.Contains(out, "boom")) tst.AssertTrue(t, strings.Contains(out, err.UniqueID)) }