github.com/joomcode/pegomock@v2.9.2-0.20220414140958-14f53b6b2a6c+incompatible/pegomock/main_test.go (about)

     1  // Copyright 2016 Peter Goetz
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main_test
    16  
    17  import (
    18  	"bytes"
    19  	"go/build"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	kingpin "gopkg.in/alecthomas/kingpin.v2"
    25  
    26  	. "github.com/onsi/ginkgo"
    27  	. "github.com/onsi/gomega"
    28  	main "github.com/petergtz/pegomock/pegomock"
    29  
    30  	. "github.com/petergtz/pegomock/pegomock/testutil"
    31  
    32  	"testing"
    33  )
    34  
    35  var (
    36  	joinPath = filepath.Join
    37  )
    38  
    39  func TestPegomock(t *testing.T) {
    40  	RegisterFailHandler(Fail)
    41  	RunSpecs(t, "CLI Suite")
    42  }
    43  
    44  var (
    45  	_ = describeCLIWithGoModulesEnabled(false)
    46  	_ = describeCLIWithGoModulesEnabled(true)
    47  )
    48  
    49  func describeCLIWithGoModulesEnabled(useGoModules bool) interface{} {
    50  	mode := "with GOPATH"
    51  	if useGoModules {
    52  		mode = "with Go modules"
    53  	}
    54  
    55  	Describe("CLI "+mode, func() {
    56  
    57  		var (
    58  			packageDir, subPackageDir, vendorPackageDir string
    59  			app                                         *kingpin.Application
    60  			origWorkingDir                              string
    61  			done                                        chan bool = make(chan bool)
    62  		)
    63  
    64  		BeforeEach(func() {
    65  			if !useGoModules {
    66  				packageDir = joinPath(build.Default.GOPATH, "src", "pegomocktest")
    67  			} else {
    68  				tmpDir, e := filepath.EvalSymlinks(os.TempDir())
    69  				Expect(e).NotTo(HaveOccurred())
    70  				packageDir = filepath.Join(tmpDir, "pegomocktest")
    71  			}
    72  			Expect(os.MkdirAll(packageDir, 0755)).To(Succeed())
    73  			subPackageDir = joinPath(packageDir, "subpackage")
    74  			Expect(os.MkdirAll(subPackageDir, 0755)).To(Succeed())
    75  			vendorPackageDir = joinPath(packageDir, "vendor", "github.com", "petergtz", "vendored_package")
    76  			Expect(os.MkdirAll(vendorPackageDir, 0755)).To(Succeed())
    77  
    78  			var e error
    79  			origWorkingDir, e = os.Getwd()
    80  			Expect(e).NotTo(HaveOccurred())
    81  			os.Chdir(packageDir)
    82  
    83  			if useGoModules {
    84  				WriteFile(joinPath(packageDir, "go.mod"),
    85  					`module pegomocktest
    86  					go 1.12
    87  					require (
    88  						github.com/onsi/gomega v1.5.0 // indirect
    89  						github.com/petergtz/pegomock v2.3.0+incompatible
    90  					)`)
    91  			}
    92  
    93  			WriteFile(joinPath(packageDir, "mydisplay.go"),
    94  				"package pegomocktest; type MyDisplay interface {  Show(something string) }")
    95  			WriteFile(joinPath(packageDir, "http_request_handler.go"),
    96  				`package pegomocktest; import "net/http"; type RequestHandler interface {  Handler(r *http.Request) }`)
    97  			WriteFile(joinPath(subPackageDir, "subdisplay.go"),
    98  				"package subpackage; type SubDisplay interface {  ShowMe() }")
    99  			if !useGoModules {
   100  				WriteFile(joinPath(vendorPackageDir, "iface.go"),
   101  					`package vendored_package; type Interface interface{ Foobar() }`)
   102  				WriteFile(joinPath(packageDir, "vendordisplay.go"), `package pegomocktest
   103  					import ( "github.com/petergtz/vendored_package" )
   104  					type VendorDisplay interface { Show(something vendored_package.Interface) }`)
   105  			}
   106  
   107  			app = kingpin.New("pegomock", "Generates mocks based on interfaces.")
   108  			app.Terminate(func(int) { panic("Unexpected terminate") })
   109  		})
   110  
   111  		AfterEach(func() {
   112  			Expect(os.RemoveAll(packageDir)).To(Succeed())
   113  			os.Chdir(origWorkingDir)
   114  		})
   115  
   116  		Describe(`"generate" command`, func() {
   117  
   118  			Context(`with args "MyDisplay"`, func() {
   119  
   120  				It(`generates a file mock_mydisplay_test.go that contains "package pegomocktest_test"`, func() {
   121  					// The rationale behind this is:
   122  					// mocks should always be part of test packages, because we don't
   123  					// want them to be part of the production code.
   124  					// But to be useful, they must still reside in the package, where
   125  					// they are actually used.
   126  
   127  					main.Run(cmd("pegomock generate MyDisplay"), os.Stdout, os.Stdin, app, done)
   128  
   129  					Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll(
   130  						BeAnExistingFile(),
   131  						BeAFileContainingSubString("package pegomocktest_test")))
   132  				})
   133  
   134  				It(`sets the default mock name "DisplayMock"`, func() {
   135  					main.Run(cmd("pegomock generate MyDisplay"), os.Stdout, os.Stdin, app, done)
   136  
   137  					Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll(
   138  						BeAnExistingFile(),
   139  						BeAFileContainingSubString("MockMyDisplay")))
   140  				})
   141  			})
   142  
   143  			Context(`with args "VendorDisplay""`, func() {
   144  
   145  				It(`generates a file mock_vendordisplay_test.go that contains 'import ( vendored_package "github.com/petergtz/vendored_package" )'`, func() {
   146  					if useGoModules {
   147  						Skip("Vendoring not supported with Go modules yet")
   148  					}
   149  
   150  					main.Run(cmd("pegomock generate -m VendorDisplay"), os.Stdout, os.Stdin, app, done)
   151  					Expect(joinPath(packageDir, "mock_vendordisplay_test.go")).To(SatisfyAll(
   152  						BeAnExistingFile(),
   153  						BeAFileContainingSubString(`vendored_package "github.com/petergtz/vendored_package"`)))
   154  					Expect(joinPath(packageDir, "matchers", "vendored_package_interface.go")).To(SatisfyAll(
   155  						BeAnExistingFile(),
   156  						BeAFileContainingSubString(`vendored_package "github.com/petergtz/vendored_package"`)))
   157  				})
   158  			})
   159  
   160  			Context(`with args "pegomocktest/subpackage SubDisplay"`, func() {
   161  				It(`generates a file mock_subdisplay_test.go in "pegomocktest" that contains "package pegomocktest_test"`, func() {
   162  					main.Run(cmd("pegomock generate pegomocktest/subpackage SubDisplay"), os.Stdout, os.Stdin, app, done)
   163  
   164  					Expect(joinPath(packageDir, "mock_subdisplay_test.go")).To(SatisfyAll(
   165  						BeAnExistingFile(),
   166  						BeAFileContainingSubString("package pegomocktest_test")))
   167  				})
   168  			})
   169  
   170  			Context("with args mydisplay.go", func() {
   171  				It(`generates a file mock_mydisplay_test.go that contains "package pegomocktest_test"`, func() {
   172  					main.Run(cmd("pegomock generate mydisplay.go"), os.Stdout, os.Stdin, app, done)
   173  
   174  					Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll(
   175  						BeAnExistingFile(),
   176  						BeAFileContainingSubString("package pegomocktest_test"),
   177  						BeAFileContainingSubString("MockMyDisplay"),
   178  					))
   179  				})
   180  			})
   181  
   182  			Context("with args -d mydisplay.go", func() {
   183  				It(`prints out debug information on stdout`, func() {
   184  					var buf bytes.Buffer
   185  					main.Run(cmd("pegomock generate -d mydisplay.go"), &buf, os.Stdin, app, done)
   186  					Expect(buf.String()).To(ContainSubstring("- method Show"))
   187  				})
   188  			})
   189  
   190  			Context("with args -o where output override is a path with a non-existing directory", func() {
   191  				It(`creates an output directory before code generation`, func() {
   192  					var buf bytes.Buffer
   193  					main.Run(cmd("pegomock generate mydisplay.go -o testoutput/test.go"), &buf, os.Stdin, app, done)
   194  					Expect(joinPath(packageDir, "testoutput/test.go")).To(SatisfyAll(
   195  						BeAnExistingFile(),
   196  						BeAFileContainingSubString("package pegomocktest_test")))
   197  				})
   198  			})
   199  
   200  			Context("with args --output-dir", func() {
   201  				It(`creates the mocks in output dir with the dir's basename as package name`, func() {
   202  					var buf bytes.Buffer
   203  					main.Run(cmd("pegomock generate MyDisplay --output-dir fakes"), &buf, os.Stdin, app, done)
   204  					Expect(joinPath(packageDir, "fakes/mock_mydisplay.go")).To(SatisfyAll(
   205  						BeAnExistingFile(),
   206  						BeAFileContainingSubString("package fakes")))
   207  				})
   208  			})
   209  
   210  			Context("with args --output-dir and --package", func() {
   211  				It(`creates the mocks in output dir with the specified package name`, func() {
   212  					var buf bytes.Buffer
   213  					main.Run(cmd("pegomock generate MyDisplay --output-dir fakes --package other"), &buf, os.Stdin, app, done)
   214  
   215  					Expect(joinPath(packageDir, "fakes/mock_mydisplay.go")).To(SatisfyAll(
   216  						BeAnExistingFile(),
   217  						BeAFileContainingSubString("package other")))
   218  				})
   219  			})
   220  
   221  			Context("with args --mock-name", func() {
   222  				It(`sets the mock name as given`, func() {
   223  					main.Run(cmd("pegomock generate MyDisplay --mock-name RenamedMock"), os.Stdout, os.Stdin, app, done)
   224  
   225  					Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll(
   226  						BeAnExistingFile(),
   227  						BeAFileContainingSubString("RenamedMock")))
   228  				})
   229  			})
   230  
   231  			Context("with args for specifying matcher directory", func() {
   232  				It(`creates matchers in the specified directory`, func() {
   233  					if useGoModules {
   234  						Skip("Vendoring not supported with Go modules yet")
   235  					}
   236  					var buf bytes.Buffer
   237  					main.Run(cmd("pegomock generate --generate-matchers --matchers-dir custom/matcher/dir VendorDisplay"), &buf, os.Stdin, app, done)
   238  
   239  					Expect(joinPath(packageDir, "custom/matcher/dir")).To(BeADirectory())
   240  					Expect(joinPath(packageDir, "custom/matcher/dir/vendored_package_interface.go")).To(BeAnExistingFile())
   241  				})
   242  			})
   243  
   244  			Context("with too many args", func() {
   245  
   246  				It(`reports an error and the usage`, func() {
   247  					var buf bytes.Buffer
   248  					Expect(func() {
   249  						main.Run(cmd("pegomock generate with too many args"), &buf, os.Stdin, app, done)
   250  					}).To(Panic())
   251  
   252  					Expect(buf.String()).To(ContainSubstring("Please provide exactly 1 interface or 1 package + 1 interface"))
   253  					Expect(buf.String()).To(ContainSubstring("usage"))
   254  				})
   255  			})
   256  
   257  		})
   258  
   259  		Describe(`"watch" command`, func() {
   260  
   261  			AfterEach(func(testDone Done) { done <- true; close(testDone) }, 3)
   262  
   263  			Context("with no further action", func() {
   264  				It(`Creates a template file interfaces_to_mock in the current directory`, func() {
   265  					go main.Run(cmd("pegomock watch"), os.Stdout, os.Stdin, app, done)
   266  					Eventually(func() string { return "interfaces_to_mock" }, "3s").Should(BeAnExistingFile())
   267  				})
   268  			})
   269  
   270  			Context("after populating interfaces_to_mock with an actual interface", func() {
   271  				It(`Eventually creates a file mock_mydisplay_test.go starting with "package pegomocktest_test"`, func() {
   272  					WriteFile(joinPath(packageDir, "interfaces_to_mock"), "MyDisplay")
   273  
   274  					go main.Run(cmd("pegomock watch"), os.Stdout, os.Stdin, app, done)
   275  
   276  					Eventually(joinPath(packageDir, "mock_mydisplay_test.go"), "3s").Should(SatisfyAll(
   277  						BeAnExistingFile(),
   278  						BeAFileContainingSubString("package pegomocktest_test")))
   279  				})
   280  			})
   281  
   282  		})
   283  
   284  		Describe(`"remove" command`, func() {
   285  			Context("there are no mock files", func() {
   286  				It("removes mock files in current directory only", func() {
   287  					var buf bytes.Buffer
   288  
   289  					main.Run(cmd("pegomock remove -n"), &buf, os.Stdin, app, done)
   290  
   291  					Expect(buf.String()).To(ContainSubstring(`No files to remove.`))
   292  				})
   293  			})
   294  
   295  			Context("there are some mock files", func() {
   296  				BeforeEach(func() {
   297  					main.Run(cmd("pegomock generate MyDisplay"), os.Stdout, os.Stdin, app, done)
   298  					Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll(
   299  						BeAnExistingFile(),
   300  						BeAFileContainingSubString("package pegomocktest_test")))
   301  
   302  					main.Run(cmd("pegomock generate --output-dir "+subPackageDir+" pegomocktest/subpackage SubDisplay"), os.Stdout, os.Stdin, app, done)
   303  
   304  					Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(SatisfyAll(
   305  						BeAnExistingFile(),
   306  						BeAFileContainingSubString("package subpackage")))
   307  				})
   308  
   309  				Context("Non-interactive", func() {
   310  					Context("non-recursive", func() {
   311  						It("removes mock files in current directory only", func() {
   312  							var buf bytes.Buffer
   313  
   314  							main.Run(cmd("pegomock remove -n"), &buf, os.Stdin, app, done)
   315  
   316  							Expect(buf.String()).To(ContainSubstring(`Deleting the following files:
   317  ` + packageDir + `/mock_mydisplay_test.go`))
   318  							Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile())
   319  							Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile())
   320  						})
   321  					})
   322  
   323  					Context("recursive", func() {
   324  						It("removes mock files recursively", func() {
   325  							var buf bytes.Buffer
   326  
   327  							main.Run(cmd("pegomock remove -n -r"), &buf, os.Stdin, app, done)
   328  
   329  							Expect(buf.String()).To(ContainSubstring(`Deleting the following files:
   330  ` + packageDir + `/mock_mydisplay_test.go
   331  ` + packageDir + `/subpackage/mock_subdisplay.go`))
   332  							Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile())
   333  							Expect(joinPath(subPackageDir, "mock_subdisplay.go")).NotTo(BeAnExistingFile())
   334  						})
   335  
   336  						Context("custom matchers were generated", func() {
   337  							BeforeEach(func() {
   338  								main.Run(cmd("pegomock generate -m RequestHandler"), os.Stdout, os.Stdin, app, done)
   339  								Expect(joinPath(packageDir, "mock_requesthandler_test.go")).To(SatisfyAll(
   340  									BeAnExistingFile(),
   341  									BeAFileContainingSubString("package pegomocktest_test")))
   342  
   343  								Expect(joinPath(packageDir, "matchers", "ptr_to_http_request.go")).To(SatisfyAll(
   344  									BeAnExistingFile(),
   345  									BeAFileContainingSubString("package matchers"),
   346  								))
   347  								main.Run(cmd("pegomock generate -m --output-dir "+subPackageDir+" RequestHandler"), os.Stdout, os.Stdin, app, done)
   348  
   349  								Expect(joinPath(subPackageDir, "mock_requesthandler.go")).To(SatisfyAll(
   350  									BeAnExistingFile(),
   351  									BeAFileContainingSubString("package subpackage")))
   352  								Expect(joinPath(subPackageDir, "matchers", "ptr_to_http_request.go")).To(SatisfyAll(
   353  									BeAnExistingFile(),
   354  									BeAFileContainingSubString("package matchers"),
   355  								))
   356  
   357  							})
   358  
   359  							It("removes matchers and matchers dir too", func() {
   360  								var buf bytes.Buffer
   361  
   362  								main.Run(cmd("pegomock remove -n -r"), &buf, os.Stdin, app, done)
   363  
   364  								Expect(buf.String()).To(Equal(`Deleting the following files:
   365  `+packageDir+`/matchers
   366  `+packageDir+`/matchers/ptr_to_http_request.go
   367  `+packageDir+`/mock_mydisplay_test.go
   368  `+packageDir+`/mock_requesthandler_test.go
   369  `+packageDir+`/subpackage/matchers
   370  `+packageDir+`/subpackage/matchers/ptr_to_http_request.go
   371  `+packageDir+`/subpackage/mock_requesthandler.go
   372  `+packageDir+`/subpackage/mock_subdisplay.go
   373  `), buf.String())
   374  								Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile())
   375  								Expect(joinPath(subPackageDir, "mock_subdisplay.go")).NotTo(BeAnExistingFile())
   376  							})
   377  						})
   378  					})
   379  
   380  					Context("Silent", func() {
   381  						It("removes mock files, but provides no output", func() {
   382  							var buf bytes.Buffer
   383  
   384  							main.Run(cmd("pegomock remove -n --silent"), &buf, os.Stdin, app, done)
   385  
   386  							Expect(buf.String()).To(BeEmpty())
   387  							Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile())
   388  							Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile())
   389  						})
   390  					})
   391  
   392  					Context("with path", func() {
   393  						It("removes mock files in path", func() {
   394  							var buf bytes.Buffer
   395  
   396  							main.Run(cmd("pegomock remove -n "+subPackageDir), &buf, os.Stdin, app, done)
   397  
   398  							Expect(buf.String()).To(ContainSubstring(`Deleting the following files:
   399  ` + packageDir + `/subpackage/mock_subdisplay.go`))
   400  							Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(BeAnExistingFile())
   401  							Expect(joinPath(subPackageDir, "mock_subdisplay.go")).NotTo(BeAnExistingFile())
   402  						})
   403  					})
   404  				})
   405  
   406  				Context("Interactive", func() {
   407  					Context("confirming with yes", func() {
   408  						It("removes mock files", func() {
   409  							var buf bytes.Buffer
   410  
   411  							main.Run(cmd("pegomock remove"), &buf, strings.NewReader("yes\n"), app, done)
   412  
   413  							Expect(buf.String()).To(ContainSubstring(`Will delete the following files:
   414  ` + packageDir + `/mock_mydisplay_test.go
   415  Continue? [y/n]:`))
   416  							Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile())
   417  							Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile())
   418  						})
   419  					})
   420  
   421  					Context("confirming with no", func() {
   422  						It("does not remove mock files", func() {
   423  							var buf bytes.Buffer
   424  
   425  							main.Run(cmd("pegomock remove"), &buf, strings.NewReader("no\n"), app, done)
   426  
   427  							Expect(buf.String()).To(ContainSubstring(`Will delete the following files:
   428  ` + packageDir + `/mock_mydisplay_test.go
   429  Continue? [y/n]:`))
   430  							Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(BeAnExistingFile())
   431  							Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile())
   432  						})
   433  					})
   434  				})
   435  
   436  				Context("dry-run", func() {
   437  					It("removes no mock files, but provides files that would be deleted", func() {
   438  						var buf bytes.Buffer
   439  
   440  						main.Run(cmd("pegomock remove --dry-run"), &buf, os.Stdin, app, done)
   441  
   442  						Expect(buf.String()).To(ContainSubstring(`Would delete the following files:
   443  ` + packageDir + `/mock_mydisplay_test.go`))
   444  						Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(BeAnExistingFile())
   445  						Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile())
   446  					})
   447  				})
   448  			})
   449  		})
   450  
   451  		Context("with some unknown command", func() {
   452  			It(`reports an error and the usage`, func() {
   453  				var buf bytes.Buffer
   454  				kingpin.CommandLine.Terminate(nil)
   455  				kingpin.CommandLine.Writer(&buf)
   456  
   457  				main.Run(cmd("pegomock some unknown command"), &buf, os.Stdin, app, done)
   458  				Expect(buf.String()).To(ContainSubstring("error"))
   459  			})
   460  		})
   461  
   462  	})
   463  	return nil
   464  }
   465  func cmd(line string) []string {
   466  	return strings.Split(line, " ")
   467  }