How to use Smuggle method of td Package

Best Go-testdeep code snippet using td.Smuggle

td_smuggle.go

Source:td_smuggle.go Github

copy

Full Screen

...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}...

Full Screen

Full Screen

td_smuggle_test.go

Source:td_smuggle_test.go Github

copy

Full Screen

...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}...

Full Screen

Full Screen

router_behavior_test.go

Source:router_behavior_test.go Github

copy

Full Screen

...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}

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

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 (

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

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}

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

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: 1

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

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}

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

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}

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 rand.Seed(time.Now().UnixNano())4 fmt.Println("Hello, playground")5 td.Smuggle()6}

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

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}

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

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 of

Full Screen

Full Screen

Smuggle

Using AI Code Generation

copy

Full Screen

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()

Full Screen

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Go-testdeep automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Most used method in

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful