Best Go-testdeep code snippet using td.Smuggle
td_smuggle.go
Source:td_smuggle.go  
...16	"unicode/utf8"17	"github.com/maxatome/go-testdeep/internal/ctxerr"18	"github.com/maxatome/go-testdeep/internal/types"19)20// SmuggledGot can be returned by a [Smuggle] function to name the21// transformed / returned value.22type SmuggledGot struct {23	Name string24	Got  any25}26const smuggled = "<smuggled>"27var (28	smuggleFnsMu sync.Mutex29	smuggleFns   = map[any]reflect.Value{}30	nilError = reflect.New(types.Error).Elem()31)32func (s SmuggledGot) contextAndGot(ctx ctxerr.Context) (ctxerr.Context, reflect.Value) {33	// If the Name starts with a Letter, prefix it by a "."34	var name string35	if s.Name != "" {36		first, _ := utf8.DecodeRuneInString(s.Name)37		if unicode.IsLetter(first) {38			name = "."39		}40		name += s.Name41	} else {42		name = smuggled43	}44	return ctx.AddCustomLevel(name), reflect.ValueOf(s.Got)45}46type tdSmuggle struct {47	tdSmugglerBase48	function reflect.Value49	argType  reflect.Type50}51var _ TestDeep = &tdSmuggle{}52type smuggleValue struct {53	Path  string54	Value reflect.Value55}56var smuggleValueType = reflect.TypeOf(smuggleValue{})57type smuggleField struct {58	Name    string59	Indexed bool60}61func joinFieldsPath(path []smuggleField) string {62	var buf strings.Builder63	for i, part := range path {64		if part.Indexed {65			fmt.Fprintf(&buf, "[%s]", part.Name)66		} else {67			if i > 0 {68				buf.WriteByte('.')69			}70			buf.WriteString(part.Name)71		}72	}73	return buf.String()74}75func splitFieldsPath(origPath string) ([]smuggleField, error) {76	if origPath == "" {77		return nil, fmt.Errorf("FIELD_PATH cannot be empty")78	}79	var res []smuggleField80	for path := origPath; len(path) > 0; {81		r, _ := utf8.DecodeRuneInString(path)82		switch r {83		case '[':84			path = path[1:]85			end := strings.IndexByte(path, ']')86			if end < 0 {87				return nil, fmt.Errorf("cannot find final ']' in FIELD_PATH %q", origPath)88			}89			res = append(res, smuggleField{Name: path[:end], Indexed: true})90			path = path[end+1:]91		case '.':92			if len(res) == 0 {93				return nil, fmt.Errorf("'.' cannot be the first rune in FIELD_PATH %q", origPath)94			}95			path = path[1:]96			if path == "" {97				return nil, fmt.Errorf("final '.' in FIELD_PATH %q is not allowed", origPath)98			}99			r, _ = utf8.DecodeRuneInString(path)100			if r == '.' || r == '[' {101				return nil, fmt.Errorf("unexpected %q after '.' in FIELD_PATH %q", r, origPath)102			}103			fallthrough104		default:105			var field string106			end := strings.IndexAny(path, ".[")107			if end < 0 {108				field, path = path, ""109			} else {110				field, path = path[:end], path[end:]111			}112			for j, r := range field {113				if !unicode.IsLetter(r) && (j == 0 || !unicode.IsNumber(r)) {114					return nil, fmt.Errorf("unexpected %q in field name %q in FIELDS_PATH %q", r, field, origPath)115				}116			}117			res = append(res, smuggleField{Name: field})118		}119	}120	return res, nil121}122func nilFieldErr(path []smuggleField) error {123	return fmt.Errorf("field %q is nil", joinFieldsPath(path))124}125func buildFieldsPathFn(path string) (func(any) (smuggleValue, error), error) {126	parts, err := splitFieldsPath(path)127	if err != nil {128		return nil, err129	}130	return func(got any) (smuggleValue, error) {131		vgot := reflect.ValueOf(got)132		for idxPart, field := range parts {133			// Resolve all interface and pointer dereferences134			for {135				switch vgot.Kind() {136				case reflect.Interface, reflect.Ptr:137					if vgot.IsNil() {138						return smuggleValue{}, nilFieldErr(parts[:idxPart])139					}140					vgot = vgot.Elem()141					continue142				}143				break144			}145			if !field.Indexed {146				if vgot.Kind() == reflect.Struct {147					vgot = vgot.FieldByName(field.Name)148					if !vgot.IsValid() {149						return smuggleValue{}, fmt.Errorf(150							"field %q not found",151							joinFieldsPath(parts[:idxPart+1]))152					}153					continue154				}155				if idxPart == 0 {156					return smuggleValue{},157						fmt.Errorf("it is a %s and should be a struct", vgot.Kind())158				}159				return smuggleValue{}, fmt.Errorf(160					"field %q is a %s and should be a struct",161					joinFieldsPath(parts[:idxPart]), vgot.Kind())162			}163			switch vgot.Kind() {164			case reflect.Map:165				tkey := vgot.Type().Key()166				var vkey reflect.Value167				switch tkey.Kind() {168				case reflect.String:169					vkey = reflect.ValueOf(field.Name)170				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:171					i, err := strconv.ParseInt(field.Name, 10, 64)172					if err != nil {173						return smuggleValue{}, fmt.Errorf(174							"field %q, %q is not an integer and so cannot match %s map key type",175							joinFieldsPath(parts[:idxPart+1]), field.Name, tkey)176					}177					vkey = reflect.ValueOf(i).Convert(tkey)178				case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:179					i, err := strconv.ParseUint(field.Name, 10, 64)180					if err != nil {181						return smuggleValue{}, fmt.Errorf(182							"field %q, %q is not an unsigned integer and so cannot match %s map key type",183							joinFieldsPath(parts[:idxPart+1]), field.Name, tkey)184					}185					vkey = reflect.ValueOf(i).Convert(tkey)186				case reflect.Float32, reflect.Float64:187					f, err := strconv.ParseFloat(field.Name, 64)188					if err != nil {189						return smuggleValue{}, fmt.Errorf(190							"field %q, %q is not a float and so cannot match %s map key type",191							joinFieldsPath(parts[:idxPart+1]), field.Name, tkey)192					}193					vkey = reflect.ValueOf(f).Convert(tkey)194				case reflect.Complex64, reflect.Complex128:195					c, err := strconv.ParseComplex(field.Name, 128)196					if err != nil {197						return smuggleValue{}, fmt.Errorf(198							"field %q, %q is not a complex number and so cannot match %s map key type",199							joinFieldsPath(parts[:idxPart+1]), field.Name, tkey)200					}201					vkey = reflect.ValueOf(c).Convert(tkey)202				default:203					return smuggleValue{}, fmt.Errorf(204						"field %q, %q cannot match unsupported %s map key type",205						joinFieldsPath(parts[:idxPart+1]), field.Name, tkey)206				}207				vgot = vgot.MapIndex(vkey)208				if !vgot.IsValid() {209					return smuggleValue{}, fmt.Errorf("field %q, %q map key not found",210						joinFieldsPath(parts[:idxPart+1]), field.Name)211				}212			case reflect.Slice, reflect.Array:213				i, err := strconv.ParseInt(field.Name, 10, 64)214				if err != nil {215					return smuggleValue{}, fmt.Errorf(216						"field %q, %q is not a slice/array index",217						joinFieldsPath(parts[:idxPart+1]), field.Name)218				}219				if i < 0 {220					i = int64(vgot.Len()) + i221				}222				if i < 0 || i >= int64(vgot.Len()) {223					return smuggleValue{}, fmt.Errorf(224						"field %q, %d is out of slice/array range (len %d)",225						joinFieldsPath(parts[:idxPart+1]), i, vgot.Len())226				}227				vgot = vgot.Index(int(i))228			default:229				if idxPart == 0 {230					return smuggleValue{},231						fmt.Errorf("it is a %s, but a map, array or slice is expected",232							vgot.Kind())233				}234				return smuggleValue{}, fmt.Errorf(235					"field %q is a %s, but a map, array or slice is expected",236					joinFieldsPath(parts[:idxPart]), vgot.Kind())237			}238		}239		return smuggleValue{240			Path:  path,241			Value: vgot,242		}, nil243	}, nil244}245func getFieldsPathFn(fieldPath string) (reflect.Value, error) {246	smuggleFnsMu.Lock()247	defer smuggleFnsMu.Unlock()248	if vfn, ok := smuggleFns[fieldPath]; ok {249		return vfn, nil250	}251	fn, err := buildFieldsPathFn(fieldPath)252	if err != nil {253		return reflect.Value{}, err254	}255	vfn := reflect.ValueOf(fn)256	smuggleFns[fieldPath] = vfn257	return vfn, err258}259func getCaster(outType reflect.Type) reflect.Value {260	smuggleFnsMu.Lock()261	defer smuggleFnsMu.Unlock()262	if vfn, ok := smuggleFns[outType]; ok {263		return vfn264	}265	var fn reflect.Value266	switch outType.Kind() {267	case reflect.String:268		fn = buildCaster(outType, true)269	case reflect.Slice:270		if outType.Elem().Kind() == reflect.Uint8 {271			// Special case for slices of bytes: falls back on io.Reader if not []byte272			fn = buildCaster(outType, false)273			break274		}275		fallthrough276	default:277		// For all other types, take the received param and return278		// it. Smuggle already converted got to the type of param, so the279		// work is done.280		inOut := []reflect.Type{outType}281		fn = reflect.MakeFunc(282			reflect.FuncOf(inOut, inOut, false),283			func(args []reflect.Value) []reflect.Value { return args },284		)285	}286	smuggleFns[outType] = fn287	return fn288}289// buildCaster returns a function:290//291//	func(in any) (out outType, err error)292//293// dynamically checksâ¦294//   - if useString is false, as outType is a slice of bytes:295//     1. in is a []byte or convertible to []byte296//     2. in implements io.Reader297//   - if useString is true, as outType is a string:298//     1. in is a []byte or convertible to string299//     2. in implements io.Reader300func buildCaster(outType reflect.Type, useString bool) reflect.Value {301	zeroRet := reflect.New(outType).Elem()302	return reflect.MakeFunc(303		reflect.FuncOf(304			[]reflect.Type{types.Interface},305			[]reflect.Type{outType, types.Error},306			false,307		),308		func(args []reflect.Value) []reflect.Value {309			if args[0].IsNil() {310				return []reflect.Value{311					zeroRet,312					reflect.ValueOf(&ctxerr.Error{313						Message:  "incompatible parameter type",314						Got:      types.RawString("nil"),315						Expected: types.RawString(outType.String() + " or convertible or io.Reader"),316					}),317				}318			}319			// 1st & only arg is always an interface320			args[0] = args[0].Elem()321			if ok, convertible := types.IsTypeOrConvertible(args[0], outType); ok {322				if convertible {323					return []reflect.Value{args[0].Convert(outType), nilError}324				}325				return []reflect.Value{args[0], nilError}326			}327			// Our caller encures Interface() can be called safely328			switch ta := args[0].Interface().(type) {329			case io.Reader:330				var b bytes.Buffer331				if _, err := b.ReadFrom(ta); err != nil {332					return []reflect.Value{333						zeroRet,334						reflect.ValueOf(&ctxerr.Error{335							Message: "an error occurred while reading from io.Reader",336							Summary: ctxerr.NewSummary(err.Error()),337						}),338					}339				}340				var buf any341				if useString {342					buf = b.String()343				} else {344					buf = b.Bytes()345				}346				return []reflect.Value{347					reflect.ValueOf(buf).Convert(outType),348					nilError,349				}350			default:351				return []reflect.Value{352					zeroRet,353					reflect.ValueOf(&ctxerr.Error{354						Message:  "incompatible parameter type",355						Got:      types.RawString(args[0].Type().String()),356						Expected: types.RawString(outType.String() + " or convertible or io.Reader"),357					}),358				}359			}360		})361}362// summary(Smuggle): changes data contents or mutates it into another363// type via a custom function or a struct fields-path before stepping364// down in favor of generic comparison process365// input(Smuggle): all366// Smuggle operator allows to change data contents or mutate it into367// another type before stepping down in favor of generic comparison368// process. Of course it is a smuggler operator. So fn is a function369// that must take one parameter whose type must be convertible to the370// type of the compared value.371//372// As convenient shortcuts, fn can be a string specifying a373// fields-path through structs, maps & slices, or any other type, in374// this case a simple cast is done (see below for details).375//376// fn must return at least one value. These value will be compared as is377// to expectedValue, here integer 28:378//379//	td.Cmp(t, "0028",380//	  td.Smuggle(func(value string) int {381//	    num, _ := strconv.Atoi(value)382//	    return num383//	  }, 28),384//	)385//386// or using an other [TestDeep] operator, here [Between](28, 30):387//388//	td.Cmp(t, "0029",389//	  td.Smuggle(func(value string) int {390//	    num, _ := strconv.Atoi(value)391//	    return num392//	  }, td.Between(28, 30)),393//	)394//395// fn can return a second boolean value, used to tell that a problem396// occurred and so stop the comparison:397//398//	td.Cmp(t, "0029",399//	  td.Smuggle(func(value string) (int, bool) {400//	    num, err := strconv.Atoi(value)401//	    return num, err == nil402//	  }, td.Between(28, 30)),403//	)404//405// fn can return a third string value which is used to describe the406// test when a problem occurred (false second boolean value):407//408//	td.Cmp(t, "0029",409//	  td.Smuggle(func(value string) (int, bool, string) {410//	    num, err := strconv.Atoi(value)411//	    if err != nil {412//	      return 0, false, "string must contain a number"413//	    }414//	    return num, true, ""415//	  }, td.Between(28, 30)),416//	)417//418// Instead of returning (X, bool) or (X, bool, string), fn can419// return (X, error). When a problem occurs, the returned error is420// non-nil, as in:421//422//	td.Cmp(t, "0029",423//	  td.Smuggle(func(value string) (int, error) {424//	    num, err := strconv.Atoi(value)425//	    return num, err426//	  }, td.Between(28, 30)),427//	)428//429// Which can be simplified to:430//431//	td.Cmp(t, "0029", td.Smuggle(strconv.Atoi, td.Between(28, 30)))432//433// Imagine you want to compare that the Year of a date is between 2010434// and 2020:435//436//	td.Cmp(t, time.Date(2015, time.May, 1, 1, 2, 3, 0, time.UTC),437//	  td.Smuggle(func(date time.Time) int { return date.Year() },438//	    td.Between(2010, 2020)),439//	)440//441// In this case the data location forwarded to next test will be442// something like "DATA.MyTimeField<smuggled>", but you can act on it443// too by returning a [SmuggledGot] struct (by value or by address):444//445//	td.Cmp(t, time.Date(2015, time.May, 1, 1, 2, 3, 0, time.UTC),446//	  td.Smuggle(func(date time.Time) SmuggledGot {447//	    return SmuggledGot{448//	      Name: "Year",449//	      Got:  date.Year(),450//	    }451//	  }, td.Between(2010, 2020)),452//	)453//454// then the data location forwarded to next test will be something like455// "DATA.MyTimeField.Year". The "." between the current path (here456// "DATA.MyTimeField") and the returned Name "Year" is automatically457// added when Name starts with a Letter.458//459// Note that [SmuggledGot] and [*SmuggledGot] returns are treated460// equally, and they are only used when fn has only one returned value461// or when the second boolean returned value is true.462//463// Of course, all cases can go together:464//465//	// Accepts a "YYYY/mm/DD HH:MM:SS" string to produce a time.Time and tests466//	// whether this date is contained between 2 hours before now and now.467//	td.Cmp(t, "2020-01-25 12:13:14",468//	  td.Smuggle(func(date string) (*SmuggledGot, bool, string) {469//	    date, err := time.Parse("2006/01/02 15:04:05", date)470//	    if err != nil {471//	      return nil, false, `date must conform to "YYYY/mm/DD HH:MM:SS" format`472//	    }473//	    return &SmuggledGot{474//	      Name: "Date",475//	      Got:  date,476//	    }, true, ""477//	  }, td.Between(time.Now().Add(-2*time.Hour), time.Now())),478//	)479//480// or:481//482//	// Accepts a "YYYY/mm/DD HH:MM:SS" string to produce a time.Time and tests483//	// whether this date is contained between 2 hours before now and now.484//	td.Cmp(t, "2020-01-25 12:13:14",485//	  td.Smuggle(func(date string) (*SmuggledGot, error) {486//	    date, err := time.Parse("2006/01/02 15:04:05", date)487//	    if err != nil {488//	      return nil, err489//	    }490//	    return &SmuggledGot{491//	      Name: "Date",492//	      Got:  date,493//	    }, nil494//	  }, td.Between(time.Now().Add(-2*time.Hour), time.Now())),495//	)496//497// Smuggle can also be used to access a struct field embedded in498// several struct layers.499//500//	type A struct{ Num int }501//	type B struct{ As map[string]*A }502//	type C struct{ B B }503//	got := C{B: B{As: map[string]*A{"foo": {Num: 12}}}}504//505//	// Tests that got.B.A.Num is 12506//	td.Cmp(t, got,507//	  td.Smuggle(func(c C) int {508//	    return c.B.As["foo"].Num509//	  }, 12))510//511// As brought up above, a fields-path can be passed as fn value512// instead of a function pointer. Using this feature, the [Cmp]513// call in the above example can be rewritten as follows:514//515//	// Tests that got.B.As["foo"].Num is 12516//	td.Cmp(t, got, td.Smuggle("B.As[foo].Num", 12))517//518// Contrary to [JSONPointer] operator, private fields can be519// followed. Arrays, slices and maps work using the index/key inside520// square brackets (e.g. [12] or [foo]). Maps work only for simple key521// types (string or numbers), without "" when using strings522// (e.g. [foo]).523//524// Behind the scenes, a temporary function is automatically created to525// achieve the same goal, but add some checks against nil values and526// auto-dereference interfaces and pointers, even on several levels,527// like in:528//529//	type A struct{ N any }530//	num := 12531//	pnum := &num532//	td.Cmp(t, A{N: &pnum}, td.Smuggle("N", 12))533//534// Last but not least, a simple type can be passed as fn to operate535// a cast, handling specifically strings and slices of bytes:536//537//	td.Cmp(t, `{"foo":1}`, td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":1}`)))538//	// or equally539//	td.Cmp(t, `{"foo":1}`, td.Smuggle(json.RawMessage(nil), td.JSON(`{"foo":1}`)))540//541// converts on the fly a string to a [json.RawMessage] so [JSON] operator542// can parse it as JSON. This is mostly a shortcut for:543//544//	td.Cmp(t, `{"foo":1}`, td.Smuggle(545//	  func(r json.RawMessage) json.RawMessage { return r },546//	  td.JSON(`{"foo":1}`)))547//548// except that for strings and slices of bytes (like here), it accepts549// [io.Reader] interface too:550//551//	var body io.Reader552//	// â¦553//	td.Cmp(t, body, td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":1}`)))554//	// or equally555//	td.Cmp(t, body, td.Smuggle(json.RawMessage(nil), td.JSON(`{"foo":1}`)))556//557// This last example allows to easily inject body content into JSON558// operator.559//560// The difference between Smuggle and [Code] operators is that [Code]561// is used to do a final comparison while Smuggle transforms the data562// and then steps down in favor of generic comparison563// process. Moreover, the type accepted as input for the function is564// more lax to facilitate the writing of tests (e.g. the function can565// accept a float64 and the got value be an int). See examples. On the566// other hand, the output type is strict and must match exactly the567// expected value type. The fields-path string fn shortcut and the568// cast feature are not available with [Code] operator.569//570// TypeBehind method returns the [reflect.Type] of only parameter of571// fn. For the case where fn is a fields-path, it is always572// any, as the type can not be known in advance.573//574// See also [Code] and [JSONPointer].575//576// [json.RawMessage]: https://pkg.go.dev/encoding/json#RawMessage577func Smuggle(fn, expectedValue any) TestDeep {578	s := tdSmuggle{579		tdSmugglerBase: newSmugglerBase(expectedValue),580	}581	const usage = "(FUNC|FIELDS_PATH|ANY_TYPE, TESTDEEP_OPERATOR|EXPECTED_VALUE)"582	const fullUsage = "Smuggle" + usage583	var vfn reflect.Value584	switch rfn := fn.(type) {585	case reflect.Type:586		switch rfn.Kind() {587		case reflect.Func, reflect.Invalid, reflect.Interface:588			s.err = ctxerr.OpBad("Smuggle",589				"usage: Smuggle%s, ANY_TYPE reflect.Type cannot be Func nor Interface", usage)590			return &s591		default:592			vfn = getCaster(rfn)593		}594	case string:595		if rfn == "" {596			vfn = getCaster(reflect.TypeOf(fn))597			break598		}599		var err error600		vfn, err = getFieldsPathFn(rfn)601		if err != nil {602			s.err = ctxerr.OpBad("Smuggle", "Smuggle%s: %s", usage, err)603			return &s604		}605	default:606		vfn = reflect.ValueOf(fn)607		switch vfn.Kind() {608		case reflect.Func:609			// nothing to check610		case reflect.Invalid, reflect.Interface:611			s.err = ctxerr.OpBad("Smuggle",612				"usage: Smuggle%s, ANY_TYPE cannot be nil nor Interface", usage)613			return &s614		default:615			vfn = getCaster(vfn.Type())616		}617	}618	fnType := vfn.Type()619	if fnType.IsVariadic() || fnType.NumIn() != 1 {620		s.err = ctxerr.OpBad("Smuggle", fullUsage+": FUNC must take only one non-variadic argument")621		return &s622	}623	switch fnType.NumOut() {624	case 3: // (value, bool, string)625		if fnType.Out(2).Kind() != reflect.String {626			break627		}628		fallthrough629	case 2:630		// (value, *bool*) or (value, *bool*, string)631		if fnType.Out(1).Kind() != reflect.Bool &&632			// (value, *error*)633			(fnType.NumOut() > 2 ||634				fnType.Out(1) != types.Error) {635			break636		}637		fallthrough638	case 1: // (value)639		if vfn.IsNil() {640			s.err = ctxerr.OpBad("Smuggle", "Smuggle(FUNC): FUNC cannot be a nil function")641			return &s642		}643		s.argType = fnType.In(0)644		s.function = vfn645		if !s.isTestDeeper {646			s.expectedValue = reflect.ValueOf(expectedValue)647		}648		return &s649	}650	s.err = ctxerr.OpBad("Smuggle",651		fullUsage+": FUNC must return value or (value, bool) or (value, bool, string) or (value, error)")652	return &s653}654func (s *tdSmuggle) laxConvert(got reflect.Value) (reflect.Value, bool) {655	if got.IsValid() {656		if types.IsConvertible(got, s.argType) {657			return got.Convert(s.argType), true658		}659	} else if s.argType == types.Interface {660		// nil only accepted if any expected661		return reflect.New(types.Interface).Elem(), true662	}663	return got, false664}665func (s *tdSmuggle) Match(ctx ctxerr.Context, got reflect.Value) *ctxerr.Error {666	if s.err != nil {667		return ctx.CollectError(s.err)668	}669	got, ok := s.laxConvert(got)670	if !ok {671		if ctx.BooleanError {672			return ctxerr.BooleanError673		}674		err := ctxerr.Error{675			Message:  "incompatible parameter type",676			Expected: types.RawString(s.argType.String()),677		}678		if got.IsValid() {679			err.Got = types.RawString(got.Type().String())680		} else {681			err.Got = types.RawString("nil")682		}683		return ctx.CollectError(&err)684	}685	// Refuse to override unexported fields access in this case. It is a686	// choice, as we think it is better to work on surrounding struct687	// instead.688	if !got.CanInterface() {689		if ctx.BooleanError {690			return ctxerr.BooleanError691		}692		return ctx.CollectError(&ctxerr.Error{693			Message: "cannot smuggle unexported field",694			Summary: ctxerr.NewSummary("work on surrounding struct instead"),695		})696	}697	ret := s.function.Call([]reflect.Value{got})698	if len(ret) == 1 ||699		(ret[1].Kind() == reflect.Bool && ret[1].Bool()) ||700		(ret[1].Kind() == reflect.Interface && ret[1].IsNil()) {701		newGot := ret[0]702		var newCtx ctxerr.Context703		if newGot.IsValid() {704			switch newGot.Type() {705			case smuggledGotType:706				newCtx, newGot = newGot.Interface().(SmuggledGot).contextAndGot(ctx)707			case smuggledGotPtrType:708				if smGot := newGot.Interface().(*SmuggledGot); smGot == nil {709					newCtx, newGot = ctx, reflect.ValueOf(nil)710				} else {711					newCtx, newGot = smGot.contextAndGot(ctx)712				}713			case smuggleValueType:714				smv := newGot.Interface().(smuggleValue)715				newCtx, newGot = ctx.AddCustomLevel("."+smv.Path), smv.Value716			default:717				newCtx = ctx.AddCustomLevel(smuggled)718			}719		}720		return deepValueEqual(newCtx, newGot, s.expectedValue)721	}722	if ctx.BooleanError {723		return ctxerr.BooleanError724	}725	var reason string726	switch len(ret) {727	case 3: // (value, false, string)728		reason = ret[2].String()729	case 2:730		// (value, error)731		if ret[1].Kind() == reflect.Interface {732			// For internal use only733			if cErr, ok := ret[1].Interface().(*ctxerr.Error); ok {734				return ctx.CollectError(cErr)735			}736			reason = ret[1].Interface().(error).Error()737		}738		// (value, false)739	}740	return ctx.CollectError(&ctxerr.Error{741		Message: "ran smuggle code with %% as argument",742		Summary: ctxerr.NewSummaryReason(got, reason),743	})744}745func (s *tdSmuggle) HandleInvalid() bool {746	return true // Knows how to handle untyped nil values (aka invalid values)747}748func (s *tdSmuggle) String() string {749	if s.err != nil {750		return s.stringError()751	}752	return "Smuggle(" + s.function.Type().String() + ")"753}754func (s *tdSmuggle) TypeBehind() reflect.Type {755	if s.err != nil {756		return nil757	}758	return s.argType759}...td_smuggle_test.go
Source:td_smuggle_test.go  
...31	}32	return33}34func (r *reArmReader) String() string { return "<no string here>" }35func TestSmuggle(t *testing.T) {36	num := 4237	gotStruct := MyStruct{38		MyStructMid: MyStructMid{39			MyStructBase: MyStructBase{40				ValBool: true,41			},42			ValStr: "foobar",43		},44		ValInt: 123,45		Ptr:    &num,46	}47	gotTime, err := time.Parse(time.RFC3339, "2018-05-23T12:13:14Z")48	if err != nil {49		t.Fatal(err)50	}51	//52	// One returned value53	checkOK(t,54		gotTime,55		td.Smuggle(56			func(date time.Time) int {57				return date.Year()58			},59			td.Between(2010, 2020)))60	checkOK(t,61		gotStruct,62		td.Smuggle(63			func(s MyStruct) td.SmuggledGot {64				return td.SmuggledGot{65					Name: "ValStr",66					Got:  s.ValStr,67				}68			},69			td.Contains("oob")))70	checkOK(t,71		gotStruct,72		td.Smuggle(73			func(s MyStruct) *td.SmuggledGot {74				return &td.SmuggledGot{75					Name: "ValStr",76					Got:  s.ValStr,77				}78			},79			td.Contains("oob")))80	//81	// 2 returned values82	checkOK(t,83		gotStruct,84		td.Smuggle(85			func(s MyStruct) (string, bool) {86				if s.ValStr == "" {87					return "", false88				}89				return s.ValStr, true90			},91			td.Contains("oob")))92	checkOK(t,93		gotStruct,94		td.Smuggle(95			func(s MyStruct) (td.SmuggledGot, bool) {96				if s.ValStr == "" {97					return td.SmuggledGot{}, false98				}99				return td.SmuggledGot{100					Name: "ValStr",101					Got:  s.ValStr,102				}, true103			},104			td.Contains("oob")))105	checkOK(t,106		gotStruct,107		td.Smuggle(108			func(s MyStruct) (*td.SmuggledGot, bool) {109				if s.ValStr == "" {110					return nil, false111				}112				return &td.SmuggledGot{113					Name: "ValStr",114					Got:  s.ValStr,115				}, true116			},117			td.Contains("oob")))118	//119	// 3 returned values120	checkOK(t,121		gotStruct,122		td.Smuggle(123			func(s MyStruct) (string, bool, string) {124				if s.ValStr == "" {125					return "", false, "ValStr must not be empty"126				}127				return s.ValStr, true, ""128			},129			td.Contains("oob")))130	checkOK(t,131		gotStruct,132		td.Smuggle(133			func(s MyStruct) (td.SmuggledGot, bool, string) {134				if s.ValStr == "" {135					return td.SmuggledGot{}, false, "ValStr must not be empty"136				}137				return td.SmuggledGot{138					Name: "ValStr",139					Got:  s.ValStr,140				}, true, ""141			},142			td.Contains("oob")))143	checkOK(t,144		gotStruct,145		td.Smuggle(146			func(s MyStruct) (*td.SmuggledGot, bool, string) {147				if s.ValStr == "" {148					return nil, false, "ValStr must not be empty"149				}150				return &td.SmuggledGot{151					Name: "ValStr",152					Got:  s.ValStr,153				}, true, ""154			},155			td.Contains("oob")))156	//157	// Convertible types158	checkOK(t, 123,159		td.Smuggle(func(n float64) int { return int(n) }, 123))160	type xInt int161	checkOK(t, xInt(123),162		td.Smuggle(func(n int) int64 { return int64(n) }, int64(123)))163	checkOK(t, xInt(123),164		td.Smuggle(func(n uint32) int64 { return int64(n) }, int64(123)))165	checkOK(t, int32(123),166		td.Smuggle(func(n int64) int { return int(n) }, 123))167	checkOK(t, gotTime,168		td.Smuggle(func(t fmt.Stringer) string { return t.String() },169			"2018-05-23 12:13:14 +0000 UTC"))170	checkOK(t, []byte("{}"),171		td.Smuggle(172			func(x json.RawMessage) json.RawMessage { return x },173			td.JSON(`{}`)))174	//175	// bytes slice caster variations176	checkOK(t, []byte(`{"foo":1}`),177		td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":1}`)))178	checkOK(t, []byte(`{"foo":1}`),179		td.Smuggle(json.RawMessage(nil), td.JSON(`{"foo":1}`)))180	checkOK(t, []byte(`{"foo":1}`),181		td.Smuggle(reflect.TypeOf(json.RawMessage(nil)), td.JSON(`{"foo":1}`)))182	checkOK(t, `{"foo":1}`,183		td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":1}`)))184	checkOK(t, newReArmReader([]byte(`{"foo":1}`)), // io.Reader first185		td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":1}`)))186	checkError(t, nil,187		td.Smuggle(json.RawMessage{}, td.JSON(`{}`)),188		expectedError{189			Message:  mustBe("incompatible parameter type"),190			Path:     mustBe("DATA"),191			Got:      mustBe("nil"),192			Expected: mustBe("json.RawMessage or convertible or io.Reader"),193		})194	checkError(t, MyStruct{},195		td.Smuggle(json.RawMessage{}, td.JSON(`{}`)),196		expectedError{197			Message:  mustBe("incompatible parameter type"),198			Path:     mustBe("DATA"),199			Got:      mustBe("td_test.MyStruct"),200			Expected: mustBe("json.RawMessage or convertible or io.Reader"),201		})202	checkError(t, errReader{}, // erroneous io.Reader203		td.Smuggle(json.RawMessage{}, td.JSON(`{}`)),204		expectedError{205			Message: mustBe("an error occurred while reading from io.Reader"),206			Path:    mustBe("DATA"),207			Summary: mustBe("an error occurred"),208		})209	//210	// strings caster variations211	type myString string212	checkOK(t, `pipo bingo`,213		td.Smuggle("", td.HasSuffix("bingo")))214	checkOK(t, []byte(`pipo bingo`),215		td.Smuggle(myString(""), td.HasSuffix("bingo")))216	checkOK(t, []byte(`pipo bingo`),217		td.Smuggle(reflect.TypeOf(myString("")), td.HasSuffix("bingo")))218	checkOK(t, newReArmReader([]byte(`pipo bingo`)), // io.Reader first219		td.Smuggle(myString(""), td.HasSuffix("bingo")))220	checkError(t, nil,221		td.Smuggle("", "bingo"),222		expectedError{223			Message:  mustBe("incompatible parameter type"),224			Path:     mustBe("DATA"),225			Got:      mustBe("nil"),226			Expected: mustBe("string or convertible or io.Reader"),227		})228	checkError(t, MyStruct{},229		td.Smuggle(myString(""), "bingo"),230		expectedError{231			Message:  mustBe("incompatible parameter type"),232			Path:     mustBe("DATA"),233			Got:      mustBe("td_test.MyStruct"),234			Expected: mustBe("td_test.myString or convertible or io.Reader"),235		})236	checkError(t, errReader{}, // erroneous io.Reader237		td.Smuggle("", "bingo"),238		expectedError{239			Message: mustBe("an error occurred while reading from io.Reader"),240			Path:    mustBe("DATA"),241			Summary: mustBe("an error occurred"),242		})243	//244	// Any other caster variations245	checkOK(t, `pipo bingo`,246		td.Smuggle([]rune{}, td.Contains([]rune(`bing`))))247	checkOK(t, `pipo bingo`,248		td.Smuggle(([]rune)(nil), td.Contains([]rune(`bing`))))249	checkOK(t, `pipo bingo`,250		td.Smuggle(reflect.TypeOf([]rune{}), td.Contains([]rune(`bing`))))251	checkOK(t, 123.456, td.Smuggle(int64(0), int64(123)))252	checkOK(t, 123.456, td.Smuggle(reflect.TypeOf(int64(0)), int64(123)))253	//254	// Errors255	checkError(t, "123",256		td.Smuggle(func(n float64) int { return int(n) }, 123),257		expectedError{258			Message:  mustBe("incompatible parameter type"),259			Path:     mustBe("DATA"),260			Got:      mustBe("string"),261			Expected: mustBe("float64"),262		})263	checkError(t, nil,264		td.Smuggle(func(n int64) int { return int(n) }, 123),265		expectedError{266			Message:  mustBe("incompatible parameter type"),267			Path:     mustBe("DATA"),268			Got:      mustBe("nil"),269			Expected: mustBe("int64"),270		})271	checkError(t, 12,272		td.Smuggle(func(n int) (int, bool) { return n, false }, 12),273		expectedError{274			Message: mustBe("ran smuggle code with %% as argument"),275			Path:    mustBe("DATA"),276			Summary: mustBe("  value: 12\nit failed but didn't say why"),277		})278	type MyBool bool279	type MyString string280	checkError(t, 12,281		td.Smuggle(func(n int) (int, MyBool, MyString) {282			return n, false, "very custom error"283		}, 12),284		expectedError{285			Message: mustBe("ran smuggle code with %% as argument"),286			Path:    mustBe("DATA"),287			Summary: mustBe("        value: 12\nit failed coz: very custom error"),288		})289	checkError(t, 12,290		td.Smuggle(func(n int) (int, error) {291			return n, errors.New("very custom error")292		}, 12),293		expectedError{294			Message: mustBe("ran smuggle code with %% as argument"),295			Path:    mustBe("DATA"),296			Summary: mustBe("        value: 12\nit failed coz: very custom error"),297		})298	checkError(t, 12,299		td.Smuggle(func(n int) *td.SmuggledGot { return nil }, int64(13)),300		expectedError{301			Message:  mustBe("values differ"),302			Path:     mustBe("DATA"),303			Got:      mustBe("nil"),304			Expected: mustBe("(int64) 13"),305		})306	// Internal use307	checkError(t, 12,308		td.Smuggle(func(n int) (int, error) {309			return n, &ctxerr.Error{310				Message: "my message",311				Summary: ctxerr.NewSummary("my summary"),312			}313		}, 13),314		expectedError{315			Message: mustBe("my message"),316			Path:    mustBe("DATA"),317			Summary: mustBe("my summary"),318		})319	//320	// Errors behind Smuggle()321	checkError(t, 12,322		td.Smuggle(func(n int) int64 { return int64(n) }, int64(13)),323		expectedError{324			Message:  mustBe("values differ"),325			Path:     mustBe("DATA<smuggled>"),326			Got:      mustBe("(int64) 12"),327			Expected: mustBe("(int64) 13"),328		})329	checkError(t, gotStruct,330		td.Smuggle("MyStructMid.MyStructBase.ValBool", false),331		expectedError{332			Message:  mustBe("values differ"),333			Path:     mustBe("DATA.MyStructMid.MyStructBase.ValBool"),334			Got:      mustBe("true"),335			Expected: mustBe("false"),336		})337	checkError(t, 12,338		td.Smuggle(func(n int) td.SmuggledGot {339			return td.SmuggledGot{340				// With Name = ""341				Got: int64(n),342			}343		}, int64(13)),344		expectedError{345			Message:  mustBe("values differ"),346			Path:     mustBe("DATA<smuggled>"),347			Got:      mustBe("(int64) 12"),348			Expected: mustBe("(int64) 13"),349		})350	checkError(t, 12,351		td.Smuggle(func(n int) *td.SmuggledGot {352			return &td.SmuggledGot{353				Name: "<int64>",354				Got:  int64(n),355			}356		}, int64(13)),357		expectedError{358			Message:  mustBe("values differ"),359			Path:     mustBe("DATA<int64>"), // no dot added between DATA and <int64>360			Got:      mustBe("(int64) 12"),361			Expected: mustBe("(int64) 13"),362		})363	checkError(t, 12,364		td.Smuggle(func(n int) *td.SmuggledGot {365			return &td.SmuggledGot{366				Name: "Int64",367				Got:  int64(n),368			}369		}, int64(13)),370		expectedError{371			Message:  mustBe("values differ"),372			Path:     mustBe("DATA.Int64"), // dot added between DATA and Int64373			Got:      mustBe("(int64) 12"),374			Expected: mustBe("(int64) 13"),375		})376	//377	// Bad usage378	const usage = "Smuggle(FUNC|FIELDS_PATH|ANY_TYPE, TESTDEEP_OPERATOR|EXPECTED_VALUE): "379	checkError(t, "never tested",380		td.Smuggle(nil, 12),381		expectedError{382			Message: mustBe("bad usage of Smuggle operator"),383			Path:    mustBe("DATA"),384			Summary: mustBe("usage: " + usage[:len(usage)-2] + ", ANY_TYPE cannot be nil nor Interface"),385		})386	checkError(t, nil,387		td.Smuggle(reflect.TypeOf((*fmt.Stringer)(nil)).Elem(), 1234),388		expectedError{389			Message: mustBe("bad usage of Smuggle operator"),390			Path:    mustBe("DATA"),391			Summary: mustBe("usage: " + usage[:len(usage)-2] + ", ANY_TYPE reflect.Type cannot be Func nor Interface"),392		})393	checkError(t, nil,394		td.Smuggle(reflect.TypeOf(func() {}), 1234),395		expectedError{396			Message: mustBe("bad usage of Smuggle operator"),397			Path:    mustBe("DATA"),398			Summary: mustBe("usage: " + usage[:len(usage)-2] + ", ANY_TYPE reflect.Type cannot be Func nor Interface"),399		})400	checkError(t, "never tested",401		td.Smuggle((func(string) int)(nil), 12),402		expectedError{403			Message: mustBe("bad usage of Smuggle operator"),404			Path:    mustBe("DATA"),405			Summary: mustBe("Smuggle(FUNC): FUNC cannot be a nil function"),406		})407	checkError(t, "never tested",408		td.Smuggle("bad[path", 12),409		expectedError{410			Message: mustBe("bad usage of Smuggle operator"),411			Path:    mustBe("DATA"),412			Summary: mustBe(usage + `cannot find final ']' in FIELD_PATH "bad[path"`),413		})414	// Bad number of args415	checkError(t, "never tested",416		td.Smuggle(func() int { return 0 }, 12),417		expectedError{418			Message: mustBe("bad usage of Smuggle operator"),419			Path:    mustBe("DATA"),420			Summary: mustBe(usage + "FUNC must take only one non-variadic argument"),421		})422	checkError(t, "never tested",423		td.Smuggle(func(x ...int) int { return 0 }, 12),424		expectedError{425			Message: mustBe("bad usage of Smuggle operator"),426			Path:    mustBe("DATA"),427			Summary: mustBe(usage + "FUNC must take only one non-variadic argument"),428		})429	checkError(t, "never tested",430		td.Smuggle(func(a int, b string) int { return 0 }, 12),431		expectedError{432			Message: mustBe("bad usage of Smuggle operator"),433			Path:    mustBe("DATA"),434			Summary: mustBe(usage + "FUNC must take only one non-variadic argument"),435		})436	// Bad number of returned values437	const errMesg = usage + "FUNC must return value or (value, bool) or (value, bool, string) or (value, error)"438	checkError(t, "never tested",439		td.Smuggle(func(a int) {}, 12),440		expectedError{441			Message: mustBe("bad usage of Smuggle operator"),442			Path:    mustBe("DATA"),443			Summary: mustBe(errMesg),444		})445	checkError(t, "never tested",446		td.Smuggle(447			func(a int) (int, bool, string, int) { return 0, false, "", 23 },448			12),449		expectedError{450			Message: mustBe("bad usage of Smuggle operator"),451			Path:    mustBe("DATA"),452			Summary: mustBe(errMesg),453		})454	// Bad returned types455	checkError(t, "never tested",456		td.Smuggle(func(a int) (int, int) { return 0, 0 }, 12),457		expectedError{458			Message: mustBe("bad usage of Smuggle operator"),459			Path:    mustBe("DATA"),460			Summary: mustBe(errMesg),461		})462	checkError(t, "never tested",463		td.Smuggle(func(a int) (int, bool, int) { return 0, false, 23 }, 12),464		expectedError{465			Message: mustBe("bad usage of Smuggle operator"),466			Path:    mustBe("DATA"),467			Summary: mustBe(errMesg),468		})469	checkError(t, "never tested",470		td.Smuggle(func(a int) (int, error, string) { return 0, nil, "" }, 12), //nolint: staticcheck471		expectedError{472			Message: mustBe("bad usage of Smuggle operator"),473			Path:    mustBe("DATA"),474			Summary: mustBe(errMesg),475		})476	//477	// String478	test.EqualStr(t,479		td.Smuggle(func(n int) int { return 0 }, 12).String(),480		"Smuggle(func(int) int)")481	test.EqualStr(t,482		td.Smuggle(func(n int) (int, bool) { return 23, false }, 12).String(),483		"Smuggle(func(int) (int, bool))")484	test.EqualStr(t,485		td.Smuggle(func(n int) (int, error) { return 23, nil }, 12).String(),486		"Smuggle(func(int) (int, error))")487	test.EqualStr(t,488		td.Smuggle(func(n int) (int, MyBool, MyString) { return 23, false, "" }, 12).489			String(),490		"Smuggle(func(int) (int, td_test.MyBool, td_test.MyString))")491	// Erroneous op492	test.EqualStr(t,493		td.Smuggle((func(int) int)(nil), 12).String(),494		"Smuggle(<ERROR>)")495}496func TestSmuggleFieldsPath(t *testing.T) {497	num := 42498	gotStruct := MyStruct{499		MyStructMid: MyStructMid{500			MyStructBase: MyStructBase{501				ValBool: true,502			},503			ValStr: "foobar",504		},505		ValInt: 123,506		Ptr:    &num,507	}508	type A struct {509		Num int510		Str string511	}512	type C struct {513		A      A514		PA1    *A515		PA2    *A516		Iface1 any517		Iface2 any518		Iface3 any519		Iface4 any520	}521	type B struct {522		A      A523		PA     *A524		PppA   ***A525		Iface  any526		Iface2 any527		Iface3 any528		C      *C529	}530	pa := &A{Num: 3, Str: "three"}531	ppa := &pa532	b := B{533		A:      A{Num: 1, Str: "one"},534		PA:     &A{Num: 2, Str: "two"},535		PppA:   &ppa,536		Iface:  A{Num: 4, Str: "four"},537		Iface2: &ppa,538		Iface3: nil,539		C: &C{540			A:      A{Num: 5, Str: "five"},541			PA1:    &A{Num: 6, Str: "six"},542			PA2:    nil, // explicit to be clear543			Iface1: A{Num: 7, Str: "seven"},544			Iface2: &A{Num: 8, Str: "eight"},545			Iface3: nil, // explicit to be clear546			Iface4: (*A)(nil),547		},548	}549	//550	// OK551	checkOK(t, gotStruct, td.Smuggle("ValInt", 123))552	checkOK(t, gotStruct,553		td.Smuggle("MyStructMid.ValStr", td.Contains("oob")))554	checkOK(t, gotStruct,555		td.Smuggle("MyStructMid.MyStructBase.ValBool", true))556	checkOK(t, gotStruct, td.Smuggle("ValBool", true)) // thanks to composition557	// OK across pointers558	checkOK(t, b, td.Smuggle("PA.Num", 2))559	checkOK(t, b, td.Smuggle("PppA.Num", 3))560	// OK with any561	checkOK(t, b, td.Smuggle("Iface.Num", 4))562	checkOK(t, b, td.Smuggle("Iface2.Num", 3))563	checkOK(t, b, td.Smuggle("C.Iface1.Num", 7))564	checkOK(t, b, td.Smuggle("C.Iface2.Num", 8))565	// Errors566	checkError(t, 12, td.Smuggle("foo.bar", 23),567		expectedError{568			Message: mustBe("ran smuggle code with %% as argument"),569			Path:    mustBe("DATA"),570			Summary: mustBe("        value: 12\nit failed coz: it is a int and should be a struct"),571		})572	checkError(t, gotStruct, td.Smuggle("ValInt.bar", 23),573		expectedError{574			Message: mustBe("ran smuggle code with %% as argument"),575			Path:    mustBe("DATA"),576			Summary: mustContain("\nit failed coz: field \"ValInt\" is a int and should be a struct"),577		})578	checkError(t, gotStruct, td.Smuggle("MyStructMid.ValStr.foobar", 23),579		expectedError{580			Message: mustBe("ran smuggle code with %% as argument"),581			Path:    mustBe("DATA"),582			Summary: mustContain("\nit failed coz: field \"MyStructMid.ValStr\" is a string and should be a struct"),583		})584	checkError(t, gotStruct, td.Smuggle("foo.bar", 23),585		expectedError{586			Message: mustBe("ran smuggle code with %% as argument"),587			Path:    mustBe("DATA"),588			Summary: mustContain("\nit failed coz: field \"foo\" not found"),589		})590	checkError(t, b, td.Smuggle("C.PA2.Num", 456),591		expectedError{592			Message: mustBe("ran smuggle code with %% as argument"),593			Path:    mustBe("DATA"),594			Summary: mustContain("\nit failed coz: field \"C.PA2\" is nil"),595		})596	checkError(t, b, td.Smuggle("C.Iface3.Num", 456),597		expectedError{598			Message: mustBe("ran smuggle code with %% as argument"),599			Path:    mustBe("DATA"),600			Summary: mustContain("\nit failed coz: field \"C.Iface3\" is nil"),601		})602	checkError(t, b, td.Smuggle("C.Iface4.Num", 456),603		expectedError{604			Message: mustBe("ran smuggle code with %% as argument"),605			Path:    mustBe("DATA"),606			Summary: mustContain("\nit failed coz: field \"C.Iface4\" is nil"),607		})608	checkError(t, b, td.Smuggle("Iface3.Num", 456),609		expectedError{610			Message: mustBe("ran smuggle code with %% as argument"),611			Path:    mustBe("DATA"),612			Summary: mustContain("\nit failed coz: field \"Iface3\" is nil"),613		})614	// Referencing maps and array/slices615	x := B{616		Iface: map[string]any{617			"test": []int{2, 3, 4},618		},619		C: &C{620			Iface1: []any{621				map[int]any{42: []string{"pipo"}, 66: [2]string{"foo", "bar"}},622				map[int8]any{42: []string{"pipo"}},623				map[int16]any{42: []string{"pipo"}},624				map[int32]any{42: []string{"pipo"}},625				map[int64]any{42: []string{"pipo"}},626				map[uint]any{42: []string{"pipo"}},627				map[uint8]any{42: []string{"pipo"}},628				map[uint16]any{42: []string{"pipo"}},629				map[uint32]any{42: []string{"pipo"}},630				map[uint64]any{42: []string{"pipo"}},631				map[uintptr]any{42: []string{"pipo"}},632				map[float32]any{42: []string{"pipo"}},633				map[float64]any{42: []string{"pipo"}},634			},635		},636	}637	checkOK(t, x, td.Smuggle("Iface[test][1]", 3))638	checkOK(t, x, td.Smuggle("C.Iface1[0][66][1]", "bar"))639	for i := 0; i < 12; i++ {640		checkOK(t, x,641			td.Smuggle(fmt.Sprintf("C.Iface1[%d][42][0]", i), "pipo"))642		checkOK(t, x,643			td.Smuggle(fmt.Sprintf("C.Iface1[%d][42][-1]", i-12), "pipo"))644	}645	checkOK(t, x, td.Lax(td.Smuggle("PppA", nil)))646	checkOK(t, x, td.Smuggle("PppA", td.Nil()))647	//648	type D struct {649		Iface any650	}651	got := D{652		Iface: []any{653			map[complex64]any{complex(42, 0): []string{"pipo"}},654			map[complex128]any{complex(42, 0): []string{"pipo"}},655		},656	}657	for i := 0; i < 2; i++ {658		checkOK(t, got, td.Smuggle(fmt.Sprintf("Iface[%d][42][0]", i), "pipo"))659		checkOK(t, got, td.Smuggle(fmt.Sprintf("Iface[%d][42][0]", i-2), "pipo"))660	}661}662func TestSmuggleTypeBehind(t *testing.T) {663	// Type behind is the smuggle function parameter one664	equalTypes(t, td.Smuggle(func(n int) bool { return n != 0 }, true), 23)665	type MyTime time.Time666	equalTypes(t,667		td.Smuggle(668			func(t MyTime) time.Time { return time.Time(t) },669			time.Now()),670		MyTime{})671	equalTypes(t,672		td.Smuggle(func(from any) any { return from }, nil),673		reflect.TypeOf((*any)(nil)).Elem())674	equalTypes(t,675		td.Smuggle("foo.bar", nil),676		reflect.TypeOf((*any)(nil)).Elem())677	// Erroneous op678	equalTypes(t, td.Smuggle((func(int) int)(nil), 12), nil)679}...router_behavior_test.go
Source:router_behavior_test.go  
...104			},105			want: td.Struct(&http.Response{106				StatusCode: http.StatusOK,107			}, td.StructFields{108				"Body": readerSmuggle(td.Contains("<title>INetSim default HTML page</title>")),109			}),110			wantErr: false,111		},112		{113			name: "Expect 500 due to error in FS",114			args: args{115				fakeFileFS: fstest.MapFS{},116				args: []rules.Param{117					{118						String: rules.StringP("default.html"),119					},120				},121				request: new(http.Request),122			},123			want: td.Struct(&http.Response{124				StatusCode: http.StatusInternalServerError,125			}, td.StructFields{126				"Body": readerSmuggle(td.Contains("open default.html: file does not exist")),127			}),128			wantErr: false,129		},130		{131			name: "Expect error due to missing argument",132			args: args{133				fakeFileFS: nil,134				args:       []rules.Param{},135				request:    new(http.Request),136			},137			wantErr: true,138		},139		{140			name: "Expect error due to argument type mismatch",141			args: args{142				fakeFileFS: nil,143				args: []rules.Param{144					{145						Int: rules.IntP(42),146					},147				},148				request: new(http.Request),149			},150			wantErr: true,151		},152	}153	for _, tc := range tests {154		tt := tc155		t.Run(tt.name, func(t *testing.T) {156			t.Parallel()157			logger := logging.CreateTestLogger(t)158			fileHandler, err := mock.FileHandler(logger, tt.args.fakeFileFS, tt.args.args...)159			if err != nil {160				if !tt.wantErr {161					t.Errorf("StatusHandler() error = %v, wantErr %v", err, tt.wantErr)162				}163				return164			}165			recorder := httptest.NewRecorder()166			fileHandler.ServeHTTP(recorder, tt.args.request)167			td.Cmp(t, recorder.Result(), tt.want)168		})169	}170}171func TestJSONHandler(t *testing.T) {172	t.Parallel()173	type args struct {174		args    []rules.Param175		request *http.Request176	}177	tests := []struct {178		name    string179		args    args180		want    interface{}181		wantErr bool182	}{183		{184			name: "Get status empty JSON",185			args: args{186				args: []rules.Param{187					{188						String: rules.StringP(`{}`),189					},190				},191				request: new(http.Request),192			},193			want: td.Struct(&http.Response{194				StatusCode: http.StatusOK,195			}, td.StructFields{196				"Body": readerSmuggle(td.String(`{}`)),197			}),198			wantErr: false,199		},200		{201			name: "Get non-empty JSON",202			args: args{203				args: []rules.Param{204					{205						String: rules.StringP(`{"Name": "Ted Tester"}`),206					},207				},208				request: new(http.Request),209			},210			want: td.Struct(&http.Response{211				StatusCode: http.StatusOK,212			}, td.StructFields{213				"Body": readerSmuggle(td.String(`{"Name": "Ted Tester"}`)),214			}),215			wantErr: false,216		},217		{218			name: "Get nested JSON",219			args: args{220				args: []rules.Param{221					{222						String: rules.StringP(`{"Name": "Ted Tester", "Address": {"Street": "Some street 1"}}`),223					},224				},225				request: new(http.Request),226			},227			want: td.Struct(&http.Response{228				StatusCode: http.StatusOK,229			}, td.StructFields{230				"Body": readerSmuggle(td.String(`{"Name": "Ted Tester", "Address": {"Street": "Some street 1"}}`)),231			}),232			wantErr: false,233		},234		{235			name: "Get nested empty array JSON",236			args: args{237				args: []rules.Param{238					{239						String: rules.StringP(`{"Name": "Ted Tester", "Colleagues": []}`),240					},241				},242				request: new(http.Request),243			},244			want: td.Struct(&http.Response{245				StatusCode: http.StatusOK,246			}, td.StructFields{247				"Body": readerSmuggle(td.String(`{"Name": "Ted Tester", "Colleagues": []}`)),248			}),249			wantErr: false,250		},251		{252			name: "Get nested array JSON",253			args: args{254				args: []rules.Param{255					{256						String: rules.StringP(`{"Name": "Ted Tester", "Colleagues": [{"Name": "Carl"}]}`),257					},258				},259				request: new(http.Request),260			},261			want: td.Struct(&http.Response{262				StatusCode: http.StatusOK,263			}, td.StructFields{264				"Body": readerSmuggle(td.String(`{"Name": "Ted Tester", "Colleagues": [{"Name": "Carl"}]}`)),265			}),266			wantErr: false,267		},268		{269			name: "Invalid JSON",270			args: args{271				args: []rules.Param{272					{273						String: rules.StringP(`{`),274					},275				},276			},277			wantErr: true,278		},279	}280	for _, tt := range tests {281		tt := tt282		t.Run(tt.name, func(t *testing.T) {283			t.Parallel()284			logger := logging.CreateTestLogger(t)285			got, err := mock.JSONHandler(logger, nil, tt.args.args...)286			if err != nil {287				if !tt.wantErr {288					t.Errorf("JSONHandler() error = %v, wantErr %v", err, tt.wantErr)289				}290				return291			}292			recorder := httptest.NewRecorder()293			got.ServeHTTP(recorder, tt.args.request)294			td.Cmp(t, recorder.Result(), tt.want)295		})296	}297}298func readerSmuggle(expected interface{}) interface{} {299	return td.Smuggle(func(reader io.Reader) (string, error) {300		data, err := io.ReadAll(reader)301		return string(data), err302	}, expected)303}Smuggle
Using AI Code Generation
1import (2func main() {3	td := &td{}4	td.Smuggle()5}6import (7func main() {8	td := &td{}9	td.Smuggle()10}11import (12func main() {13	td := &td{}14	td.Smuggle()15}16import (17func main() {18	td := &td{}19	td.Smuggle()20}21import (22func main() {23	td := &td{}24	td.Smuggle()25}26import (27func main() {28	td := &td{}29	td.Smuggle()30}31import (32func main() {33	td := &td{}34	td.Smuggle()35}36import (37func main() {38	td := &td{}39	td.Smuggle()40}41import (42func main() {43	td := &td{}44	td.Smuggle()45}46import (47func main() {48	td := &td{}49	td.Smuggle()50}51import (52func main() {53	td := &td{}54	td.Smuggle()55}56import (57func main() {58	td := &td{}59	td.Smuggle()60}61import (Smuggle
Using AI Code Generation
1import (2func main() {3	td := new(Td)4	td.Smuggle()5}6import (7func main() {8	td := new(Td)9	td.Smuggle()10}11import (12func main() {13	td := new(Td)14	td.Smuggle()15}16import (17func main() {18	td := new(Td)19	td.Smuggle()20}21import (22func main() {23	td := new(Td)24	td.Smuggle()25}26import (27func main() {28	td := new(Td)29	td.Smuggle()30}31import (32func main() {33	td := new(Td)34	td.Smuggle()35}36import (37func main() {38	td := new(Td)39	td.Smuggle()40}41import (42func main() {43	td := new(Td)44	td.Smuggle()45}46import (47func main() {48	td := new(Td)49	td.Smuggle()50}51import (52func main() {53	td := new(Td)54	td.Smuggle()55}56import (57func main() {58	td := new(Td)59	td.Smuggle()60}Smuggle
Using AI Code Generation
1import (2type td struct {3}4func (t td) Smuggle() string {5}6func main() {7	t := td{s: "Hello", i: 1}8	v := reflect.ValueOf(t)9	fmt.Println(v.MethodByName("Smuggle").Call(nil)[0].String())10}11import (12type td struct {13}14func (t td) Smuggle() string {15}16func main() {17	t := td{s: "Hello", i: 1}18	fmt.Println(v.MethodByName("Smuggle").Call(nil)[0].String())19}20import (21type td struct {22}23func (t td) Smuggle() string {24}25func main() {26	t := td{s: "Hello", i: 1}27	fmt.Println(v.MethodByName("Smuggle").Call(nil)[0].String())28}29import (30type td struct {31}32func (t td) Smuggle() string {33}34func main() {35	t := td{s: "Hello", i: 1}36	v := reflect.ValueOf(t)37	fmt.Println(v.MethodByName("Smuggle").Call(nil)[0].String())38}39import (40type td struct {41}42func (t td) Smuggle() string {43}44func main() {45	t := td{s: "Hello", i: 1Smuggle
Using AI Code Generation
1import (2func main() {3    fmt.Println(td.Smuggle(2))4}5import (6func main() {7    fmt.Println(td.Smuggle(3))8}9import (10func main() {11    fmt.Println(td.Smuggle(4))12}13import (14func main() {15    fmt.Println(td.Smuggle(5))16}17import (18func main() {19    fmt.Println(td.Smuggle(6))20}21import (22func main() {23    fmt.Println(td.Smuggle(7))24}25import (26func main() {27    fmt.Println(td.Smuggle(8))28}29import (30func main() {31    fmt.Println(td.Smuggle(9))32}33import (34func main() {35    fmt.Println(td.Smuggle(10))36}37import (38func main() {39    fmt.Println(td.Smuggle(11))40}Smuggle
Using AI Code Generation
1import (2func main() {3	fmt.Println(strings.ToLower(s))4}5import (6func main() {7	fmt.Println(strings.ToLower(s))8}9import (10func main() {11	fmt.Println(strings.ToLower(s))12}13import (14func main() {15	fmt.Println(strings.ToLower(s))16}17import (18func main() {19	fmt.Println(strings.ToLower(s))20}21import (22func main() {23	fmt.Println(strings.ToLower(s))24}25import (26func main() {27	fmt.Println(strings.ToLower(s))28}29import (30func main() {31	fmt.Println(strings.ToLower(s))32}Smuggle
Using AI Code Generation
1import (2func main() {3	rand.Seed(time.Now().UnixNano())4	fmt.Println("Hello, playground")5	td.Smuggle()6}Smuggle
Using AI Code Generation
1import (2func main() {3	td := new(Td)4	td.Smuggle()5}6import (7func main() {8	td := new(Td)9	td.Smuggle()10}11import (12func main() {13	td := new(Td)14	td.Smuggle()15}16import (17func main() {18	td := new(Td)19	td.Smuggle()20}21import (22func main() {23	td := new(Td)24	td.Smuggle()25}26import (27func main() {28	td := new(Td)29	td.Smuggle()30}31import (32func main() {33	td := new(Td)34	td.Smuggle()35}36import (37func main() {38	td := new(Td)39	td.Smuggle()40}41import (42func main() {43	td := new(Td)44	td.Smuggle()45}Smuggle
Using AI Code Generation
1import (2func main() {3	t.Smuggle()4	fmt.Println("Smuggle method of td class is called")5}6import (7func main() {8	t.Smuggle()9	fmt.Println("Smuggle method of td class is called")10}11import (12func main() {13	t.Smuggle()14	fmt.Println("Smuggle method of td class is called")15}16import (17func main() {18	t.Smuggle()19	fmt.Println("Smuggle method of td class is called")20}21import (22func main() {23	t.Smuggle()24	fmt.Println("Smuggle method of td class is called")25}26import (27func main() {28	t.Smuggle()29	fmt.Println("Smuggle method of td class is called")30}31import (32func main() {33	t.Smuggle()34	fmt.Println("Smuggle method of td class is called")35}36import (37func main() {38	t.Smuggle()39	fmt.Println("Smuggle method of td class is called")40}41import (42func main() {43	t.Smuggle()44	fmt.Println("Smuggle method ofSmuggle
Using AI Code Generation
1import "fmt"2import "github.com/td"3func main() {4    td := td.New()5    td.Smuggle("test")6    td.Smuggle(1)7    td.Smuggle(2.5)8    items, err := td.GetItems()9    if err != nil {10        fmt.Println(err)11    }12    for _, item := range items {13        fmt.Println(item)14    }15}16import "fmt"17import "github.com/td"18func main() {19    td := td.New()20    td.Smuggle("test")21    td.Smuggle(1)22    td.Smuggle(2.5)23    items, err := td.GetItems()24    if err != nil {25        fmt.Println(err)26    }27    for _, item := range items {28        fmt.Println(item)29    }30}31import "fmt"32import "github.com/td"33func main() {34    td := td.New()35    td.Smuggle("test")36    td.Smuggle(1)37    td.Smuggle(2.5)38    items, err := td.GetItems()39    if err != nil {40        fmt.Println(err)41    }42    for _, item := range items {43        fmt.Println(item)44    }45}46import "fmt"47import "github.com/td"48func main() {49    td := td.New()Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
