Files
goext/exerr/unit_test.go
T
Mikescher 02d6894ec6
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m34s
[🤖] Add Unit-Tests
2026-04-27 16:31:29 +02:00

837 lines
24 KiB
Go

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}, "<<null>>"},
}
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))
}