From eeb9931f408287d0f66db9a595ca9706e8ec84b3 Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Wed, 10 Jan 2024 11:46:24 -0300 Subject: [PATCH] Move external dependency `mockimpl` to monorepo (#15863) #15560 Probably best to review commit by commit. First commit adds the mockimpl files, second commit amends README.md and third commit fixes golangci-lint issues. - [X] Manual QA for all new/changed functionality Tested by adding a dummy method to service.go and running `make generate-mock`. --------- Co-authored-by: Victor Lyuboslavsky --- Makefile | 1 - server/mock/datastore.go | 10 +- server/mock/mockimpl/LICENSE.txt | 21 ++ server/mock/mockimpl/README.md | 66 +++++ server/mock/mockimpl/impl.go | 424 +++++++++++++++++++++++++++++ server/mock/mockimpl/impl_test.go | 218 +++++++++++++++ server/mock/mockresult/generate.go | 2 +- server/service/mock/service.go | 6 +- 8 files changed, 738 insertions(+), 10 deletions(-) create mode 100644 server/mock/mockimpl/LICENSE.txt create mode 100644 server/mock/mockimpl/README.md create mode 100644 server/mock/mockimpl/impl.go create mode 100644 server/mock/mockimpl/impl_test.go diff --git a/Makefile b/Makefile index 49b343b44b..110f9c5757 100644 --- a/Makefile +++ b/Makefile @@ -173,7 +173,6 @@ generate-dev: .prefix NODE_ENV=development yarn run webpack --progress --watch generate-mock: .prefix - go install github.com/fleetdm/mockimpl@ecbb3041eabfc9e046a3f2e414e32c28254b75b2 go generate github.com/fleetdm/fleet/v4/server/mock github.com/fleetdm/fleet/v4/server/mock/mockresult github.com/fleetdm/fleet/v4/server/service/mock generate-doc: .prefix diff --git a/server/mock/datastore.go b/server/mock/datastore.go index 7720d7b192..b162cf17c1 100644 --- a/server/mock/datastore.go +++ b/server/mock/datastore.go @@ -6,11 +6,11 @@ import ( "github.com/fleetdm/fleet/v4/server/fleet" ) -//go:generate mockimpl -o datastore_mock.go "s *DataStore" "fleet.Datastore" -//go:generate mockimpl -o datastore_installers.go "s *InstallerStore" "fleet.InstallerStore" -//go:generate mockimpl -o nanomdm/storage.go "s *Storage" "github.com/micromdm/nanomdm/storage.AllStorage" -//go:generate mockimpl -o nanodep/storage.go "s *Storage" "github.com/micromdm/nanodep/storage.AllStorage" -//go:generate mockimpl -o scep/depot.go "d *Depot" "depot.Depot" +//go:generate go run ./mockimpl/impl.go -o datastore_mock.go "s *DataStore" "fleet.Datastore" +//go:generate go run ./mockimpl/impl.go -o datastore_installers.go "s *InstallerStore" "fleet.InstallerStore" +//go:generate go run ./mockimpl/impl.go -o nanomdm/storage.go "s *Storage" "github.com/micromdm/nanomdm/storage.AllStorage" +//go:generate go run ./mockimpl/impl.go -o nanodep/storage.go "s *Storage" "github.com/micromdm/nanodep/storage.AllStorage" +//go:generate go run ./mockimpl/impl.go -o scep/depot.go "d *Depot" "depot.Depot" var _ fleet.Datastore = (*Store)(nil) diff --git a/server/mock/mockimpl/LICENSE.txt b/server/mock/mockimpl/LICENSE.txt new file mode 100644 index 0000000000..5be3e96846 --- /dev/null +++ b/server/mock/mockimpl/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Josh Bleecher Snyder + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/server/mock/mockimpl/README.md b/server/mock/mockimpl/README.md new file mode 100644 index 0000000000..a46068bb40 --- /dev/null +++ b/server/mock/mockimpl/README.md @@ -0,0 +1,66 @@ +# mockimpl + +> The contents of this directory were copied (on December 2023) from https://github.com/fleetdm/mockimpl which was forked from https://github.com/groob/mockimpl. + +`mockimpl` generates mock method stubs for implementing an interface. +mockimpl is based on [impl](https://github.com/josharian/impl) + + +```bash +go get -u github.com/groob/mockimpl +``` + +Sample usage: + +```bash +$ impl 'f *File' io.ReadWriteCloser +// Automatically generated by mockimpl. DO NOT EDIT! + +package mock + +import "io" + +var _ io.ReadWriteCloser = (*File)(nil) + +type ReadFunc func(p []byte) (n int, err error) + +type WriteFunc func(p []byte) (n int, err error) + +type CloseFunc func() error + +type File struct { + ReadFunc ReadFunc + ReadFuncInvoked bool + + WriteFunc WriteFunc + WriteFuncInvoked bool + + CloseFunc CloseFunc + CloseFuncInvoked bool +} + +func (f *File) Read(p []byte) (n int, err error) { + f.ReadFuncInvoked = true + return f.ReadFunc(p) +} + +func (f *File) Write(p []byte) (n int, err error) { + f.WriteFuncInvoked = true + return f.WriteFunc(p) +} + +func (f *File) Close() error { + f.CloseFuncInvoked = true + return f.CloseFunc() +} + +# You can also provide a full name by specifying the package path. +# This helps in cases where the interface can't be guessed +# just from the package name and interface name. +$ impl 's *Source' golang.org/x/oauth2.TokenSource +func (s *Source) Token() (*oauth2.Token, error) { + panic("not implemented") +} +``` + +You can use `impl` from Vim with [vim-go-impl](https://github.com/rhysd/vim-go-impl) diff --git a/server/mock/mockimpl/impl.go b/server/mock/mockimpl/impl.go new file mode 100644 index 0000000000..5988d277aa --- /dev/null +++ b/server/mock/mockimpl/impl.go @@ -0,0 +1,424 @@ +// impl generates method stubs for implementing an interface. +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/build" + "go/format" + "go/parser" + "go/printer" + "go/token" + "log" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "text/template" + + "golang.org/x/tools/imports" +) + +const usage = `impl [o output.go] + +impl generates method stubs for recv to implement iface. + +Examples: + +impl 'f *File' io.Reader +impl Murmur hash.Hash + +Don't forget the single quotes around the receiver type +to prevent shell globbing. +` + +// findInterface returns the import path and identifier of an interface. +// For example, given "http.ResponseWriter", findInterface returns +// "net/http", "ResponseWriter". +// If a fully qualified interface is given, such as "net/http.ResponseWriter", +// it simply parses the input. +func findInterface(iface string) (path string, id string, err error) { + if len(strings.Fields(iface)) != 1 { + return "", "", fmt.Errorf("couldn't parse interface: %s", iface) + } + + if slash := strings.LastIndex(iface, "/"); slash > -1 { + // package path provided + dot := strings.LastIndex(iface, ".") + // make sure iface does not end with "/" (e.g. reject net/http/) + if slash+1 == len(iface) { + return "", "", fmt.Errorf("interface name cannot end with a '/' character: %s", iface) + } + // make sure iface does not end with "." (e.g. reject net/http.) + if dot+1 == len(iface) { + return "", "", fmt.Errorf("interface name cannot end with a '.' character: %s", iface) + } + // make sure iface has exactly one "." after "/" (e.g. reject net/http/httputil) + if strings.Count(iface[slash:], ".") != 1 { + return "", "", fmt.Errorf("invalid interface name: %s", iface) + } + return iface[:dot], iface[dot+1:], nil + } + + src := []byte("package hack\n" + "var i " + iface) + // If we couldn't determine the import path, goimports will + // auto fix the import path. + imp, err := imports.Process(".", src, nil) + if err != nil { + return "", "", fmt.Errorf("couldn't parse interface: %s", iface) + } + + // imp should now contain an appropriate import. + // Parse out the import and the identifier. + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", imp, 0) + if err != nil { + panic(err) + } + if len(f.Imports) == 0 { + return "", "", fmt.Errorf("unrecognized interface: %s", iface) + } + raw := f.Imports[0].Path.Value // "io" + path, err = strconv.Unquote(raw) // io + if err != nil { + panic(err) + } + decl := f.Decls[1].(*ast.GenDecl) // var i io.Reader + spec := decl.Specs[0].(*ast.ValueSpec) // i io.Reader + sel := spec.Type.(*ast.SelectorExpr) // io.Reader + id = sel.Sel.Name // Reader + return path, id, nil +} + +// Pkg is a parsed build.Package. +type Pkg struct { + *build.Package + *token.FileSet +} + +// typeSpec locates the *ast.TypeSpec for type id in the import path. +func typeSpec(path string, id string) (Pkg, *ast.TypeSpec, error) { + pkg, err := build.Import(path, "", 0) + if err != nil { + return Pkg{}, nil, fmt.Errorf("couldn't find package %s: %v", path, err) + } + + fset := token.NewFileSet() // share one fset across the whole package + for _, file := range pkg.GoFiles { + f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, file), nil, 0) + if err != nil { + continue + } + + for _, decl := range f.Decls { + decl, ok := decl.(*ast.GenDecl) + if !ok || decl.Tok != token.TYPE { + continue + } + for _, spec := range decl.Specs { + spec := spec.(*ast.TypeSpec) + if spec.Name.Name != id { + continue + } + return Pkg{Package: pkg, FileSet: fset}, spec, nil + } + } + } + return Pkg{}, nil, fmt.Errorf("type %s not found in %s", id, path) +} + +// gofmt pretty-prints e. +func (p Pkg) gofmt(e ast.Expr) string { + var buf bytes.Buffer + printer.Fprint(&buf, p.FileSet, e) + return buf.String() +} + +// fullType returns the fully qualified type of e. +// Examples, assuming package net/http: +// +// fullType(int) => "int" +// fullType(Handler) => "http.Handler" +// fullType(io.Reader) => "io.Reader" +// fullType(*Request) => "*http.Request" +func (p Pkg) fullType(e ast.Expr) string { + ast.Inspect(e, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.Ident: + // Using typeSpec instead of IsExported here would be + // more accurate, but it'd be crazy expensive, and if + // the type isn't exported, there's no point trying + // to implement it anyway. + if n.IsExported() { + n.Name = p.Package.Name + "." + n.Name + } + case *ast.SelectorExpr: + return false + } + return true + }) + return p.gofmt(e) +} + +func (p Pkg) params(field *ast.Field, defaultName string) []Param { + var params []Param + typ := p.fullType(field.Type) + for _, name := range field.Names { + params = append(params, Param{Name: name.Name, Type: typ}) + } + // Handle anonymous params + if len(params) == 0 { + params = []Param{{Type: typ, Name: defaultName}} + } + return params +} + +// Method represents a method signature. +type Method struct { + RecvShort string + Recv string + Func +} + +type Struc struct { + IName string + Func +} + +// Func represents a function signature. +type Func struct { + Name string + Params []Param + Res []Param +} + +// Param represents a parameter in a function or method signature. +type Param struct { + Name string + Type string +} + +// CalledArgument will correctly generate call to a function with a +// variadic parameter +func (p *Param) CalledArgument() string { + variadic, _ := regexp.MatchString("^[.]{3}", p.Type) + if variadic { + return p.Name + "..." + } + return p.Name +} + +func (p Pkg) funcsig(f *ast.Field) Func { + fn := Func{Name: f.Names[0].Name} + typ := f.Type.(*ast.FuncType) + if typ.Params != nil { + for pos, field := range typ.Params.List { + defaultName := fmt.Sprintf("p%d", pos) + fn.Params = append(fn.Params, p.params(field, defaultName)...) + } + } + if typ.Results != nil { + for _, field := range typ.Results.List { + fn.Res = append(fn.Res, p.params(field, "")...) + } + } + return fn +} + +// funcs returns the set of methods required to implement iface. +// It is called funcs rather than methods because the +// function descriptions are functions; there is no receiver. +func funcs(iface string) ([]Func, error) { + // Locate the interface. + path, id, err := findInterface(iface) + if err != nil { + return nil, err + } + + // Parse the package and find the interface declaration. + p, spec, err := typeSpec(path, id) + if err != nil { + return nil, fmt.Errorf("interface %s not found: %s", iface, err) + } + idecl, ok := spec.Type.(*ast.InterfaceType) + if !ok { + return nil, fmt.Errorf("not an interface: %s", iface) + } + + if idecl.Methods == nil { + return nil, fmt.Errorf("empty interface: %s", iface) + } + + var fns []Func + for _, fndecl := range idecl.Methods.List { + if len(fndecl.Names) == 0 { + // Embedded interface: recurse + embedded, err := funcs(p.fullType(fndecl.Type)) + if err != nil { + return nil, err + } + fns = append(fns, embedded...) + continue + } + + fn := p.funcsig(fndecl) + fns = append(fns, fn) + } + return fns, nil +} + +const stub = "func ({{.Recv}}) {{.Name}}" + + "({{range .Params}}{{.Name}} {{.Type}}, {{end}})" + + "({{range .Res}}{{.Name}} {{.Type}}, {{end}})" + + "{\n" + "{{.RecvShort}}.mu.Lock()" + "\n" + + "{{.RecvShort}}.{{.Name}}FuncInvoked = true" + "\n" + + "{{.RecvShort}}.mu.Unlock()" + "\n" + + "return {{.RecvShort}}.{{.Name}}Func({{range .Params}}{{.CalledArgument}}, {{end}})" + + "\n" + "}\n\n" + +var tmpl = template.Must(template.New("test").Parse(stub)) + +// genStubs prints nicely formatted method stubs +// for fns using receiver expression recv. +// If recv is not a valid receiver expression, +// genStubs will panic. +func genStubs(recv string, fns []Func) []byte { + var buf bytes.Buffer + for _, fn := range fns { + meth := Method{Recv: recv, RecvShort: shortRecv(recv), Func: fn} + tmpl.Execute(&buf, meth) //nolint:errcheck + } + + pretty, err := format.Source(buf.Bytes()) + if err != nil { + panic(err) + } + return pretty +} + +func shortRecv(recv string) string { + s := strings.SplitN(recv, "*", 2)[0] + return s +} + +const packageStr = "// Automatically generated by mockimpl. DO NOT EDIT!" + + "\n\n" + "package mock" + "\n\n" + +const str = "{{.Name}}Func {{.Name}}Func" + + "\n" + "{{.Name}}FuncInvoked bool" + + "\n\n" + +const funcTypeStr = "type {{.Name}}Func func" + + "({{range .Params}}{{.Name}} {{.Type}}, {{end}})" + + "({{range .Res}}{{.Name}} {{.Type}}, {{end}})" + + "\n\n" + +var ( + tmplStr = template.Must(template.New("testtwo").Parse(str)) + funcTypetmplStr = template.Must(template.New("funcTypetmpl").Parse(funcTypeStr)) +) + +func genStr(name string, fns []Func) []byte { + var buf bytes.Buffer + for _, fn := range fns { + meth := Struc{IName: name, Func: fn} + funcTypetmplStr.Execute(&buf, meth) //nolint:errcheck + } + buf.WriteString("type ") + buf.WriteString(name) + buf.WriteString(" struct {\n") + for _, fn := range fns { + meth := Struc{IName: name, Func: fn} + tmplStr.Execute(&buf, meth) //nolint:errcheck + } + buf.WriteString("\n") + buf.WriteString("mu sync.Mutex") + buf.WriteString("\n") + buf.WriteString("}") + + pretty, err := format.Source(buf.Bytes()) + if err != nil { + panic(err) + } + return pretty + // return buf.Bytes() +} + +// validReceiver reports whether recv is a valid receiver expression. +func validReceiver(recv string) bool { + if recv == "" { + // The parse will parse empty receivers, but we don't want to accept them, + // since it won't generate a usable code snippet. + return false + } + fset := token.NewFileSet() + _, err := parser.ParseFile(fset, "", "package hack\nfunc ("+recv+") Foo()", 0) + return err == nil +} + +func main() { + flOut := flag.String("o", "", "output file") + flag.Parse() + args := flag.Args() + if len(args) != 2 { + fmt.Fprint(os.Stderr, usage) + os.Exit(2) + } + recv, iface := args[0], args[1] + if !validReceiver(recv) { + fatal(fmt.Sprintf("invalid receiver: %q", recv)) + } + + fns, err := funcs(iface) + if err != nil { + fatal(err) + } + + src := genStubs(recv, fns) + recName := strings.SplitN(recv, " ", 2) + name := strings.TrimPrefix(recName[1], "*") + src2 := genStr(name, fns) + + path, ifaceID, err := findInterface(iface) + if err != nil { + fatal(err) + } + + var buf bytes.Buffer + fmt.Fprint(&buf, packageStr) + fmt.Fprintf(&buf, "import \"%s\"\n\n", path) + fmt.Fprintf(&buf, "var _ %s.%s = (*%s)(nil)\n\n", filepath.Base(path), ifaceID, name) + fmt.Fprint(&buf, string(src2)) + buf.WriteString("\n") + fmt.Fprint(&buf, string(src)) + pretty, err := format.Source(buf.Bytes()) + if err != nil { + panic(err) + } + imp, err := imports.Process("", pretty, nil) + if err != nil { + panic(err) + } + switch *flOut { + case "": + fmt.Println(string(imp)) + default: + f, err := os.Create(*flOut) + if err != nil { + log.Fatal(err) + } + defer f.Close() + _, err = f.Write(imp) + if err != nil { + log.Fatal(err) + } + } +} + +func fatal(msg interface{}) { + fmt.Fprintln(os.Stderr, msg) + os.Exit(1) +} diff --git a/server/mock/mockimpl/impl_test.go b/server/mock/mockimpl/impl_test.go new file mode 100644 index 0000000000..9fd626c185 --- /dev/null +++ b/server/mock/mockimpl/impl_test.go @@ -0,0 +1,218 @@ +package main + +import ( + "reflect" + "testing" +) + +type errBool bool + +func (b errBool) String() string { + if b { + return "an error" + } + return "no error" +} + +func TestFindInterface(t *testing.T) { + cases := []struct { + iface string + path string + id string + wantErr bool + }{ + {iface: "net.Conn", path: "net", id: "Conn"}, + {iface: "http.ResponseWriter", path: "net/http", id: "ResponseWriter"}, + {iface: "net.Tennis", wantErr: true}, + {iface: "a + b", wantErr: true}, + {iface: "a/b/c/", wantErr: true}, + {iface: "a/b/c/pkg", wantErr: true}, + {iface: "a/b/c/pkg.", wantErr: true}, + {iface: "a/b/c/pkg.Typ", path: "a/b/c/pkg", id: "Typ"}, + {iface: "a/b/c/pkg.Typ.Foo", wantErr: true}, + } + + for _, tt := range cases { + path, id, err := findInterface(tt.iface) + gotErr := err != nil + if tt.wantErr != gotErr { + t.Errorf("findInterface(%q).err=%v want %s", tt.iface, err, errBool(tt.wantErr)) + continue + } + if tt.path != path { + t.Errorf("findInterface(%q).path=%q want %q", tt.iface, path, tt.path) + } + if tt.id != id { + t.Errorf("findInterface(%q).id=%q want %q", tt.iface, id, tt.id) + } + } +} + +func TestTypeSpec(t *testing.T) { + // For now, just test whether we can find the interface. + cases := []struct { + path string + id string + wantErr bool + }{ + {path: "net", id: "Conn"}, + {path: "net", id: "Con", wantErr: true}, + } + + for _, tt := range cases { + p, spec, err := typeSpec(tt.path, tt.id) + gotErr := err != nil + if tt.wantErr != gotErr { + t.Errorf("typeSpec(%q, %q).err=%v want %s", tt.path, tt.id, err, errBool(tt.wantErr)) + continue + } + if err == nil { + if reflect.DeepEqual(p, Pkg{}) { + t.Errorf("typeSpec(%q, %q).pkg=Pkg{} want non-nil", tt.path, tt.id) + } + if spec == nil { + t.Errorf("typeSpec(%q, %q).spec=nil want non-nil", tt.path, tt.id) + } + } + } +} + +func TestFuncs(t *testing.T) { + cases := []struct { + iface string + want []Func + wantErr bool + }{ + { + iface: "io.ReadWriter", + want: []Func{ + { + Name: "Read", + Params: []Param{{Name: "p", Type: "[]byte"}}, + Res: []Param{ + {Name: "n", Type: "int"}, + {Name: "err", Type: "error"}, + }, + }, + { + Name: "Write", + Params: []Param{{Name: "p", Type: "[]byte"}}, + Res: []Param{ + {Name: "n", Type: "int"}, + {Name: "err", Type: "error"}, + }, + }, + }, + }, + { + iface: "http.ResponseWriter", + want: []Func{ + { + Name: "Header", + Res: []Param{{Type: "http.Header"}}, + }, + { + Name: "Write", + Params: []Param{{Type: "[]byte", Name: "p0"}}, + Res: []Param{{Type: "int"}, {Type: "error"}}, + }, + { + Name: "WriteHeader", + Params: []Param{{Type: "int", Name: "statusCode"}}, + }, + }, + }, + { + iface: "http.Handler", + want: []Func{ + { + Name: "ServeHTTP", + Params: []Param{ + {Name: "p0", Type: "http.ResponseWriter"}, + {Name: "p1", Type: "*http.Request"}, + }, + }, + }, + }, + { + iface: "ast.Node", + want: []Func{ + { + Name: "Pos", + Res: []Param{{Type: "token.Pos"}}, + }, + { + Name: "End", + Res: []Param{{Type: "token.Pos"}}, + }, + }, + }, + { + iface: "cipher.AEAD", + want: []Func{ + { + Name: "NonceSize", + Res: []Param{{Type: "int"}}, + }, + { + Name: "Overhead", + Res: []Param{{Type: "int"}}, + }, + { + Name: "Seal", + Params: []Param{ + {Name: "dst", Type: "[]byte"}, + {Name: "nonce", Type: "[]byte"}, + {Name: "plaintext", Type: "[]byte"}, + {Name: "additionalData", Type: "[]byte"}, + }, + Res: []Param{{Type: "[]byte"}}, + }, + { + Name: "Open", + Params: []Param{ + {Name: "dst", Type: "[]byte"}, + {Name: "nonce", Type: "[]byte"}, + {Name: "ciphertext", Type: "[]byte"}, + {Name: "additionalData", Type: "[]byte"}, + }, + Res: []Param{{Type: "[]byte"}, {Type: "error"}}, + }, + }, + }, + {iface: "net.Tennis", wantErr: true}, + } + + for _, tt := range cases { + fns, err := funcs(tt.iface) + gotErr := err != nil + if tt.wantErr != gotErr { + t.Errorf("funcs(%q).err=%v want %s", tt.iface, err, errBool(tt.wantErr)) + continue + } + if !reflect.DeepEqual(fns, tt.want) { + t.Errorf("funcs(%q).fns=\n%v\nwant\n%v\n", tt.iface, fns, tt.want) + } + } +} + +func TestValidReceiver(t *testing.T) { + cases := []struct { + recv string + want bool + }{ + {recv: "f", want: true}, + {recv: "F", want: true}, + {recv: "f F", want: true}, + {recv: "f *F", want: true}, + {recv: "", want: false}, + {recv: "a+b", want: false}, + } + + for _, tt := range cases { + got := validReceiver(tt.recv) + if got != tt.want { + t.Errorf("validReceiver(%q)=%t want %t", tt.recv, got, tt.want) + } + } +} diff --git a/server/mock/mockresult/generate.go b/server/mock/mockresult/generate.go index a1360ee6bc..be3fce418d 100644 --- a/server/mock/mockresult/generate.go +++ b/server/mock/mockresult/generate.go @@ -1,3 +1,3 @@ package mock -//go:generate mockimpl -o datastore_query_results.go "s *QueryResultStore" "fleet.QueryResultStore" +//go:generate go run ../mockimpl/impl.go -o datastore_query_results.go "s *QueryResultStore" "fleet.QueryResultStore" diff --git a/server/service/mock/service.go b/server/service/mock/service.go index 18b8e873cb..998b7b4ba2 100644 --- a/server/service/mock/service.go +++ b/server/service/mock/service.go @@ -1,5 +1,5 @@ package mock -//go:generate mockimpl -o service_osquery.go "s *TLSService" "fleet.OsqueryService" -//go:generate mockimpl -o service_pusher_factory.go "s *APNSPushProviderFactory" "github.com/micromdm/nanomdm/push.PushProviderFactory" -//go:generate mockimpl -o service_push_provider.go "s *APNSPushProvider" "github.com/micromdm/nanomdm/push.PushProvider" +//go:generate go run ../../mock/mockimpl/impl.go -o service_osquery.go "s *TLSService" "fleet.OsqueryService" +//go:generate go run ../../mock/mockimpl/impl.go -o service_pusher_factory.go "s *APNSPushProviderFactory" "github.com/micromdm/nanomdm/push.PushProviderFactory" +//go:generate go run ../../mock/mockimpl/impl.go -o service_push_provider.go "s *APNSPushProvider" "github.com/micromdm/nanomdm/push.PushProvider"