github.com/SAP/cloud-mta-build-tool@v1.2.27/internal/tpl/makefile_test.go (about)

     1  package tpl
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/ginkgo/extensions/table"
    12  	. "github.com/onsi/gomega"
    13  	"github.com/pkg/errors"
    14  
    15  	"github.com/SAP/cloud-mta-build-tool/internal/archive"
    16  	"github.com/SAP/cloud-mta-build-tool/internal/logs"
    17  	"github.com/SAP/cloud-mta-build-tool/internal/version"
    18  	"github.com/SAP/cloud-mta/mta"
    19  )
    20  
    21  const (
    22  	makefile = "Makefile.mta"
    23  )
    24  
    25  var _ = BeforeSuite(func() {
    26  	logs.Logger = logs.NewLogger()
    27  })
    28  
    29  func removeSpecialSymbols(b []byte) string {
    30  	s := string(b)
    31  	s = strings.Replace(s, "\r", "", -1)
    32  	return s
    33  }
    34  
    35  func getMakeFileContent(filePath string) string {
    36  	expected, _ := ioutil.ReadFile(filePath)
    37  	return removeSpecialSymbols(expected)
    38  }
    39  
    40  func escapeProjPath(parts ...string) string {
    41  	return `"$(PROJ_DIR)/` + filepath.Join(parts...) + `"`
    42  }
    43  
    44  var _ = Describe("Makefile", func() {
    45  
    46  	var (
    47  		tpl          = tplCfg{tplContent: makeVerbose, relPath: "", preContent: basePreVerbose, postContent: basePost, depDesc: "dev"}
    48  		makeFileName = "MakeFileTest.mta"
    49  	)
    50  
    51  	wd, _ := os.Getwd()
    52  	expectedMakePath := filepath.Join(wd, "testdata", "ExpectedMakeFile")
    53  	expectedMakeFileContent := getMakeFileContent(expectedMakePath)
    54  	makeFileFullPath := filepath.Join(wd, "testdata", makeFileName)
    55  
    56  	Describe("MakeFile Generation", func() {
    57  		BeforeEach(func() {
    58  			version.VersionConfig = []byte(`
    59  cli_version: 0.0.0
    60  makefile_version: 0.0.0
    61  `)
    62  		})
    63  		AfterEach(func() {
    64  			Ω(os.RemoveAll(makeFileFullPath)).Should(Succeed())
    65  			Ω(os.RemoveAll(filepath.Join(wd, "testdata", "someFolder"))).Should(Succeed())
    66  		})
    67  
    68  		Describe("ExecuteMake", func() {
    69  			AfterEach(func() {
    70  				Ω(os.RemoveAll(filepath.Join(wd, "testdata", "Makefile.mta"))).Should(Succeed())
    71  			})
    72  			It("Sanity", func() {
    73  				Ω(ExecuteMake(filepath.Join(wd, "testdata"), filepath.Join(wd, "testdata"), nil, makefile, "", os.Getwd, true)).Should(Succeed())
    74  				Ω(filepath.Join(wd, "testdata", "Makefile.mta")).Should(BeAnExistingFile())
    75  			})
    76  			It("Fails on location initialization", func() {
    77  				Ω(ExecuteMake("", filepath.Join(wd, "testdata"), nil, makefile, "", func() (string, error) {
    78  					return "", errors.New("err")
    79  				}, true)).Should(HaveOccurred())
    80  			})
    81  			It("Fails on wrong mode", func() {
    82  				Ω(ExecuteMake(filepath.Join(wd, "testdata"), filepath.Join(wd, "testdata"), nil, makefile, "wrong", os.Getwd, true)).Should(HaveOccurred())
    83  			})
    84  		})
    85  
    86  		It("createMakeFile testing", func() {
    87  			makeFilePath := filepath.Join(wd, "testdata")
    88  			file, _ := createMakeFile(makeFilePath, makeFileName)
    89  			Ω(file).ShouldNot(BeNil())
    90  			Ω(file.Close()).Should(Succeed())
    91  			Ω(makeFilePath).Should(BeAnExistingFile())
    92  			_, err := createMakeFile(makeFilePath, makeFileName)
    93  			Ω(err).Should(HaveOccurred())
    94  		})
    95  		It("Sanity", func() {
    96  			ep := dir.Loc{SourcePath: filepath.Join(wd, "testdata"), TargetPath: filepath.Join(wd, "testdata"), Descriptor: "dev"}
    97  			Ω(makeFile(&ep, &ep, &ep, nil, makeFileName, &tpl, true)).Should(Succeed())
    98  			Ω(makeFileFullPath).Should(BeAnExistingFile())
    99  			Ω(getMakeFileContent(makeFileFullPath)).Should(Equal(expectedMakeFileContent))
   100  		})
   101  		It("Create make file in folder that does not exist", func() {
   102  			ep := dir.Loc{SourcePath: filepath.Join(wd, "testdata"), TargetPath: filepath.Join(wd, "testdata", "someFolder"), Descriptor: "dev"}
   103  			Ω(makeFile(&ep, &ep, &ep, nil, makeFileName, &tpl, true)).Should(Succeed())
   104  			filename := filepath.Join(ep.GetTarget(), makeFileName)
   105  			Ω(filename).Should(BeAnExistingFile())
   106  			Ω(getMakeFileContent(filename)).Should(Equal(expectedMakeFileContent))
   107  		})
   108  		It("genMakefile testing with wrong mta yaml file", func() {
   109  			ep := dir.Loc{SourcePath: filepath.Join(wd, "testdata"), TargetPath: filepath.Join(wd, "testdata"), MtaFilename: "xxx.yaml"}
   110  			Ω(genMakefile(&ep, &ep, &ep, &ep, nil, makefile, "", true)).Should(HaveOccurred())
   111  		})
   112  		It("genMakefile testing with wrong target folder (file path)", func() {
   113  			ep := dir.Loc{SourcePath: filepath.Join(wd, "testdata"), TargetPath: filepath.Join(wd, "testdata", "mta.yaml"), MtaFilename: "xxx.yaml"}
   114  			Ω(genMakefile(&ep, &ep, &ep, &ep, nil, makefile, "", true)).Should(HaveOccurred())
   115  		})
   116  		It("genMakefile testing with wrong mode", func() {
   117  			ep := dir.Loc{SourcePath: filepath.Join(wd, "testdata")}
   118  			Ω(genMakefile(&ep, &ep, &ep, &ep, nil, makefile, "wrongMode", true)).Should(HaveOccurred())
   119  		})
   120  
   121  		DescribeTable("genMakefile should fail when there is a circular build dependency between modules", func(mode string) {
   122  			ep := dir.Loc{SourcePath: filepath.Join(wd, "testdata"), TargetPath: filepath.Join(wd, "testdata"), MtaFilename: "circular.yaml"}
   123  			Ω(genMakefile(&ep, &ep, &ep, &ep, nil, makefile, mode, true)).Should(HaveOccurred())
   124  		},
   125  			Entry("in default mode", ""),
   126  			Entry("in verbose mode", "verbose"),
   127  		)
   128  
   129  		DescribeTable("generate module build in verbose make file", func(mtaFileName, moduleName, expectedModuleCommandsGen string) {
   130  			ep := dir.Loc{SourcePath: filepath.Join(wd, "testdata", "modulegen"), TargetPath: filepath.Join(wd, "testdata"), Descriptor: "dev", MtaFilename: mtaFileName}
   131  			Ω(makeFile(&ep, &ep, &ep, nil, makeFileName, &tpl, true)).Should(Succeed())
   132  			Ω(makeFileFullPath).Should(BeAnExistingFile())
   133  			makefileContent := getMakeFileContent(makeFileFullPath)
   134  
   135  			expectedModuleGen := fmt.Sprintf(`%s: validate
   136  	@echo 'INFO building the "%s" module...'
   137  	@%s`, moduleName, moduleName, expectedModuleCommandsGen)
   138  			Ω(makefileContent).Should(ContainSubstring(removeSpecialSymbols([]byte(expectedModuleGen))))
   139  		},
   140  			Entry("module with one command", "one_command.yaml", "one_command", `$(MBT) execute -d="$(PROJ_DIR)/one_command" -c=yarn`),
   141  			Entry("module with no commands and no timeout",
   142  				"no_commands.yaml", "no_commands", `$(MBT) execute -d="$(PROJ_DIR)/no_commands"`),
   143  			Entry("module with no commands and with timeout",
   144  				"no_commands_with_timeout.yaml", "no_commands_with_timeout", `$(MBT) execute -d="$(PROJ_DIR)/no_commands_with_timeout" -t=3m`),
   145  			Entry("module with multiple commands",
   146  				"multiple_commands.yaml", "multiple_commands", `$(MBT) execute -d="$(PROJ_DIR)/multiple_commands" -c='npm install' -c=grunt -c='npm prune --production'`),
   147  			Entry("module with command and timeout",
   148  				"command_with_timeout.yaml", "command_with_timeout", `$(MBT) execute -d="$(PROJ_DIR)/command_with_timeout" -t=2s -c='sleep 1'`),
   149  			Entry("module with commands with special characters",
   150  				"commands_with_special_chars.yaml", "commands_with_special_chars", `$(MBT) execute -d="$(PROJ_DIR)/commands_with_special_chars" -c='sh -c '\''echo "a"'\' -c='echo "a\b"'`),
   151  		)
   152  
   153  		modulegen := filepath.Join(wd, "testdata", "modulegen")
   154  		DescribeTable("generate module build with dependencies in verbose make file", func(mtaFileName, moduleName, modulePath, expectedModuleDepNames string, expectedModuleDepCopyCommands string) {
   155  			ep := dir.Loc{SourcePath: modulegen, TargetPath: filepath.Join(wd, "testdata"), Descriptor: "dev", MtaFilename: mtaFileName}
   156  			Ω(makeFile(&ep, &ep, &ep, nil, makeFileName, &tpl, true)).Should(Succeed())
   157  			Ω(makeFileFullPath).Should(BeAnExistingFile())
   158  			makefileContent := getMakeFileContent(makeFileFullPath)
   159  
   160  			expectedModuleGen := fmt.Sprintf(`%s: validate %s
   161  	@echo 'INFO building the "%s" module...'%s
   162  	@$(MBT) execute -d="$(PROJ_DIR)/%s"`, moduleName, expectedModuleDepNames, moduleName, expectedModuleDepCopyCommands, modulePath)
   163  			Ω(makefileContent).Should(ContainSubstring(removeSpecialSymbols([]byte(expectedModuleGen))))
   164  		},
   165  			Entry("dependency with artifacts", "dep_with_patterns.yaml", "module1", "public", `dep`, fmt.Sprintf(`
   166  	@$(MBT) cp -s=%s -t=%s -p=dist/\* -p=some_dir -p=a\*.txt`, escapeProjPath("client"), escapeProjPath("public"))),
   167  			Entry("module with two dependencies", "two_deps.yaml", "my_proj_ui_deployer", "my_proj_ui_deployer", `ui5module1 ui5module2`, fmt.Sprintf(`
   168  	@$(MBT) cp -s=%s -t=%s -p=./\*
   169  	@$(MBT) cp -s=%s -t=%s -p=./\*`,
   170  				escapeProjPath("ui5module1", "dist"), escapeProjPath("my_proj_ui_deployer", "resources", "ui5module1"),
   171  				escapeProjPath("ui5module2", "dist"), escapeProjPath("my_proj_ui_deployer", "resources", "ui5module2"))),
   172  			Entry("dependency with target-path", "dep_with_artifacts_and_targetpath.yaml", "module1", "public", `module1-dep`, fmt.Sprintf(`
   173  	@$(MBT) cp -s=%s -t=%s -p=dist/\*`, escapeProjPath("client"), escapeProjPath("public", "client"))),
   174  			Entry("dependent module with build-result and module with artifacts and target-path", "dep_with_build_results.yaml", "module1", "public", `dep1 dep2`, fmt.Sprintf(`
   175  	@$(MBT) cp -s=%s -t=%s -p=\*
   176  	@$(MBT) cp -s=%s -t=%s -p=\*`,
   177  				escapeProjPath("client1", "dist"), escapeProjPath("public", "dep1_result"),
   178  				escapeProjPath("client2", "target/*.war"), escapeProjPath("public"))),
   179  		)
   180  	})
   181  
   182  	DescribeTable("Makefile Generation Failed", func(testPath string, testTemplateFilename string) {
   183  		wd, _ := os.Getwd()
   184  		testTemplate, _ := ioutil.ReadFile(filepath.Join(wd, "testdata", testTemplateFilename))
   185  		ep := dir.Loc{SourcePath: filepath.Join(wd, "testdata"), TargetPath: filepath.Join(wd, "testdata")}
   186  		Ω(makeFile(&ep, &ep, &ep, nil, makeFileName, &tplCfg{relPath: testPath, tplContent: testTemplate, preContent: basePreVerbose, postContent: basePost, depDesc: "dev"}, true)).Should(HaveOccurred())
   187  	},
   188  		Entry("Wrong Template", "testdata", filepath.Join("testdata", "WrongMakeTmpl.txt")),
   189  		Entry("Yaml not exists", "testdata1", "make_default.txt"),
   190  	)
   191  
   192  	DescribeTable("String in slice search", func(s string, slice []string, expected bool) {
   193  		Ω(stringInSlice(s, slice)).Should(Equal(expected))
   194  	},
   195  		Entry("positive test", "test1", []string{"test1", "foo"}, true),
   196  		Entry("negative test", "test1", []string{"--test", "foo"}, false),
   197  	)
   198  
   199  	Describe("genMakefile mode tests", func() {
   200  		DescribeTable("Positive", func(mode string, tpl tplCfg, isDep bool) {
   201  			Ω(getTplCfg(mode, isDep)).Should(Equal(tpl))
   202  		},
   203  			Entry("Default mode Dev", "", tplCfg{tplContent: makeDefault, preContent: basePreDefault, postContent: basePost}, false),
   204  			Entry("Verbose mode Dev", "verbose", tplCfg{tplContent: makeVerbose, preContent: basePreVerbose, postContent: basePost}, false),
   205  		)
   206  		It("unknown mode", func() {
   207  			_, err := getTplCfg("test", false)
   208  			Ω(err).Should(MatchError(`the "test" command is not supported`))
   209  		})
   210  	})
   211  
   212  	var absPath = func(path string) string {
   213  		s, _ := filepath.Abs(path)
   214  		return s
   215  	}
   216  	sep := string(filepath.Separator)
   217  	DescribeTable("getExtensionsArg", func(extensions []string, makefileDirPath string, expected string) {
   218  		Ω(getExtensionsArg(extensions, makefileDirPath, "-e")).Should(Equal(expected))
   219  	},
   220  		Entry("empty list returns empty string", []string{}, "", ""),
   221  		Entry("nil returns empty string", nil, "", ""),
   222  		Entry("extension path is returned relative to the makefile path when it's in the same folder",
   223  			[]string{absPath("my.mtaext")}, absPath("."), ` -e="$(CURDIR)`+sep+`my.mtaext"`),
   224  		Entry("extension path is returned relative to the makefile path when it's in an inner folder",
   225  			[]string{absPath(filepath.Join("inner", "my.mtaext"))}, absPath("."), ` -e="$(CURDIR)`+sep+"inner"+sep+`my.mtaext"`),
   226  		Entry("extension path is returned relative to the makefile path when it's in an outer folder",
   227  			[]string{absPath("my.mtaext")}, absPath("inner"), ` -e="$(CURDIR)`+sep+".."+sep+`my.mtaext"`),
   228  		Entry("extension paths are separated by a comma",
   229  			[]string{absPath("my.mtaext"), absPath("second.mtaext")}, absPath("."), ` -e="$(CURDIR)`+sep+`my.mtaext,$(CURDIR)`+sep+`second.mtaext"`),
   230  	)
   231  
   232  	It("GetModuleDeps returns error when module doesn't exist", func() {
   233  		data := templateData{File: mta.MTA{}}
   234  		_, err := data.GetModuleDeps("unknown")
   235  		Ω(err).Should(HaveOccurred())
   236  	})
   237  
   238  	It("GetModuleDeps returns error when module has dependency that doesn't exist", func() {
   239  		data := templateData{File: mta.MTA{Modules: []*mta.Module{
   240  			{
   241  				Name: "m1",
   242  				BuildParams: map[string]interface{}{
   243  					"requires": []interface{}{
   244  						map[string]interface{}{"name": "unknown"},
   245  					},
   246  				},
   247  			},
   248  		}}}
   249  		_, err := data.GetModuleDeps("m1")
   250  		Ω(err).Should(HaveOccurred())
   251  	})
   252  
   253  	It("IsNoSource fails on not existing module", func() {
   254  		data := templateData{File: mta.MTA{Modules: []*mta.Module{
   255  			{
   256  				Name: "m1",
   257  				BuildParams: map[string]interface{}{
   258  					"requires": []interface{}{
   259  						map[string]interface{}{"name": "unknown"},
   260  					},
   261  				},
   262  			},
   263  		}}}
   264  		_, err := data.IsNoSource("m2")
   265  		Ω(err).Should(HaveOccurred())
   266  	})
   267  
   268  	It("IsNoSource fails on empty path", func() {
   269  		data := templateData{File: mta.MTA{Modules: []*mta.Module{
   270  			{
   271  				Name: "m1",
   272  			},
   273  		}}}
   274  		_, err := data.IsNoSource("m1")
   275  		Ω(err).Should(HaveOccurred())
   276  	})
   277  })