This commit is contained in:
@@ -0,0 +1,836 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user