github.com/SAP/cloud-mta-build-tool@v1.2.27/integration/cloud_mta_build_tool_test.go (about)

     1  // +build integration
     2  
     3  package integration_test
     4  
     5  import (
     6  	"archive/zip"
     7  	"bufio"
     8  	"bytes"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"log"
    12  
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strings"
    18  	"time"
    19  
    20  	. "github.com/onsi/ginkgo"
    21  	. "github.com/onsi/ginkgo/extensions/table"
    22  	. "github.com/onsi/gomega"
    23  	"github.com/pkg/errors"
    24  
    25  	dir "github.com/SAP/cloud-mta-build-tool/internal/archive"
    26  	"github.com/SAP/cloud-mta/mta"
    27  )
    28  
    29  const (
    30  	demoArchiveName = "mta_demo_0.0.1.mtar"
    31  	//javaArchiveName = "com.fetcher.project_0.0.1.mtar"
    32  	binPath = "mbt"
    33  )
    34  
    35  var mbtName = ""
    36  var mbtTargetPath = ""
    37  
    38  var _ = Describe("Integration - CloudMtaBuildTool", func() {
    39  
    40  	BeforeSuite(func() {
    41  		By("Building and smoke testing mbt")
    42  		mbtName = "mbt"
    43  		buildAndInstallMBT()
    44  		smokeTestMBT()
    45  	})
    46  
    47  	AfterSuite(func() {
    48  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_demo/" + mbtName))).Should(Succeed())
    49  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_demo/Makefile.mta"))).Should(Succeed())
    50  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_demo/mtad.yaml"))).Should(Succeed())
    51  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_demo/abc.mtar"))).Should(Succeed())
    52  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_demo/mta_archives"))).Should(Succeed())
    53  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_java/myModule/target"))).Should(Succeed())
    54  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_java/Makefile.mta"))).Should(Succeed())
    55  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_java/mtad.yaml"))).Should(Succeed())
    56  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_java/mta_archives"))).Should(Succeed())
    57  		Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_demo/node/package-lock.json"))).Should(Succeed())
    58  	})
    59  
    60  	var _ = Describe("Command to provide the list of modules", func() {
    61  
    62  		It("Getting module", func() {
    63  			dir, _ := os.Getwd()
    64  			path := dir + filepath.FromSlash("/testdata/mta_demo")
    65  			bin := filepath.FromSlash(binPath)
    66  			cmdOut, cmdErr, err := execute(bin, "provide modules", path)
    67  			Ω(err).Should(Succeed(), cmdErr)
    68  			Ω(cmdOut).ShouldNot(BeNil())
    69  			Ω(cmdOut).Should(ContainSubstring("[node node-js]" + "\n"))
    70  		})
    71  
    72  		It("Command name error", func() {
    73  			dir, _ := os.Getwd()
    74  			path := dir + filepath.FromSlash("/testdata/")
    75  			bin := filepath.FromSlash(binPath)
    76  			_, _, err := execute(bin, "provide modules 2", path)
    77  			Ω(err).Should(HaveOccurred())
    78  		})
    79  	})
    80  	var _ = Describe("Generate the Makefile according to the mta.yaml file", func() {
    81  
    82  		It("Generate Makefile for mta_demo", func() {
    83  			dir, _ := os.Getwd()
    84  			path := filepath.Join(dir, "testdata", "mta_demo")
    85  			bin := filepath.FromSlash(binPath)
    86  			_, errOut, err := execute(bin, "init", path)
    87  			Ω(err).Should(Succeed(), errOut)
    88  
    89  			// Check the MakeFile was generated
    90  			Ω(filepath.Join(dir, "testdata", "mta_demo", "Makefile.mta")).Should(BeAnExistingFile())
    91  		})
    92  
    93  		It("Generate Makefile for mta_java", func() {
    94  			dir, _ := os.Getwd()
    95  			path := filepath.Join(dir, "testdata", "mta_java")
    96  			bin := filepath.FromSlash(binPath)
    97  			_, errOut, err := execute(bin, "init", path)
    98  			Ω(err).Should(Succeed(), errOut)
    99  
   100  			// Check the MakeFile was generated
   101  			Ω(filepath.Join(dir, "testdata", "mta_java", "Makefile.mta")).Should(BeAnExistingFile())
   102  		})
   103  
   104  		It("Command name error", func() {
   105  			dir, _ := os.Getwd()
   106  			path := dir + filepath.FromSlash("/testdata/mta_demo")
   107  			bin := filepath.FromSlash(binPath)
   108  			_, _, err := execute(bin, "init 2", path)
   109  			Ω(err).Should(HaveOccurred())
   110  
   111  		})
   112  	})
   113  
   114  	var _ = Describe("Generate MTAR", func() {
   115  		It("Generate MTAR with provided target and mtar name", func() {
   116  			dir, _ := os.Getwd()
   117  			Ω(os.RemoveAll(filepath.Join(dir, "testdata", "mta_demo", demoArchiveName))).Should(Succeed())
   118  			path := dir + filepath.FromSlash("/testdata/mta_demo")
   119  			bin := filepath.FromSlash("make")
   120  			cmdOut, errOut, err := execute(bin, "-f Makefile.mta p=cf mtar=abc t="+path, path)
   121  			Ω(err).Should(Succeed(), errOut)
   122  			Ω(cmdOut).ShouldNot(BeEmpty())
   123  			// Check the archive was generated
   124  			Ω(filepath.Join(dir, "testdata", "mta_demo", "abc.mtar")).Should(BeAnExistingFile())
   125  		})
   126  
   127  		It("Generate MTAR - wrong platform", func() {
   128  
   129  			dir, _ := os.Getwd()
   130  			path := dir + filepath.FromSlash("/testdata/mta_demo")
   131  			bin := filepath.FromSlash("make")
   132  			out, errOut, err := execute(bin, "-f Makefile.mta p=xxx mtar=xyz1", path)
   133  			Ω(err).Should(HaveOccurred())
   134  			Ω(out + errOut).Should(ContainSubstring(`ERROR invalid target platform "xxx"; supported platforms are: "cf", "neo", "xsa"`))
   135  			Ω(filepath.Join(dir, "testdata", "mta_demo", "mta_archives", "xyz1.mtar")).ShouldNot(BeAnExistingFile())
   136  		})
   137  
   138  		var _ = Describe("MBT build - generates Makefile and executes it", func() {
   139  
   140  			It("MBT build for mta_demo", func() {
   141  				dir, _ := os.Getwd()
   142  				path := filepath.Join(dir, "testdata", "mta_demo")
   143  				bin := filepath.FromSlash(binPath)
   144  				_, errOut, err := execute(bin, "build -p=cf", path)
   145  				Ω(err).Should(Succeed(), errOut)
   146  
   147  				// Check the MTAR was generated
   148  				validateMtaArchiveContents([]string{"node/", "node/data.zip", "node-js/", "node-js/data.zip"}, filepath.Join(path, "mta_archives", "mta_demo_0.0.1.mtar"))
   149  			})
   150  
   151  			It("MBT build - wrong platform", func() {
   152  				dir, _ := os.Getwd()
   153  				path := filepath.Join(dir, "testdata", "mta_demo")
   154  				bin := filepath.FromSlash(binPath)
   155  				_, _, err := execute(bin, "build -p=xxx", path)
   156  				Ω(err).Should(HaveOccurred())
   157  			})
   158  
   159  			It("MBT build with timeout", func() {
   160  				dir, _ := os.Getwd()
   161  				path := filepath.Join(dir, "testdata", "moduletimeout")
   162  				bin := filepath.FromSlash(binPath)
   163  
   164  				start := time.Now()
   165  				out, errOut, err := execute(bin, "build", path)
   166  				elapsed := time.Since(start)
   167  				Ω(err).Should(HaveOccurred())
   168  				Ω(out + errOut).Should(ContainSubstring("timed out"))
   169  
   170  				// Check elapsed time
   171  				Ω(elapsed).Should(BeNumerically(">=", time.Duration(5)*time.Second))
   172  				Ω(elapsed).Should(BeNumerically("<=", time.Duration(10)*time.Second))
   173  			})
   174  		})
   175  
   176  		It("Generate MTAR - unsupported platform, module removed from mtad", func() {
   177  
   178  			dir, _ := os.Getwd()
   179  			path := dir + filepath.FromSlash("/testdata/mta_demo")
   180  			bin := filepath.FromSlash("make")
   181  			_, errOut, err := execute(bin, "-f Makefile.mta p=neo mtar=xyz", path)
   182  			Ω(err).Should(Succeed(), errOut)
   183  			mtarFilename := filepath.Join(dir, "testdata", "mta_demo", "mta_archives", "xyz.mtar")
   184  			Ω(mtarFilename).Should(BeAnExistingFile())
   185  			// check that module with unsupported platform 'neo' is not presented in mtad.yaml
   186  			mtadContent, e := getFileContentFromZip(mtarFilename, "mtad.yaml")
   187  			Ω(e).Should(Succeed())
   188  			actual, e := mta.Unmarshal(mtadContent)
   189  			Ω(e).Should(Succeed())
   190  			expected, e := mta.Unmarshal([]byte(`
   191  _schema-version: "3.1"
   192  ID: mta_demo
   193  version: 0.0.1
   194  modules:
   195  - name: node-js
   196    type: nodejs
   197    path: node-js
   198    provides:
   199    - name: node-js_api
   200      properties:
   201        url: ${default-url}
   202    parameters:
   203      name: nodejs
   204  parameters:
   205    hcp-deployer-version: 1.1.0
   206  `))
   207  			Ω(e).Should(Succeed())
   208  			Ω(actual).Should(Equal(expected))
   209  		})
   210  
   211  		It("Generate MTAR for mta_demo", func() {
   212  
   213  			dir, _ := os.Getwd()
   214  			path := dir + filepath.FromSlash("/testdata/mta_demo")
   215  			bin := filepath.FromSlash("make")
   216  			_, errOut, err := execute(bin, "-f Makefile.mta p=cf", path)
   217  			Ω(err).Should(Succeed(), errOut)
   218  			// Check the archive was generated
   219  			mtarFilename := filepath.Join(dir, "testdata", "mta_demo", "mta_archives", demoArchiveName)
   220  			Ω(filepath.Join(dir, "testdata", "mta_demo", "mta_archives", demoArchiveName)).Should(BeAnExistingFile())
   221  			// check that module with unsupported platform 'cf' is presented in mtad.yaml
   222  			mtadContent, e := getFileContentFromZip(mtarFilename, "mtad.yaml")
   223  			Ω(e).Should(Succeed())
   224  			actual, e := mta.Unmarshal(mtadContent)
   225  			Ω(e).Should(Succeed())
   226  			expected, e := mta.Unmarshal([]byte(`
   227  _schema-version: "3.1"
   228  ID: mta_demo
   229  version: 0.0.1
   230  modules:
   231  - name: node
   232    type: javascript.nodejs
   233    path: node
   234    provides:
   235    - name: node_api
   236      properties:
   237        url: ${default-url}
   238  - name: node-js
   239    type: javascript.nodejs
   240    path: node-js
   241    provides:
   242    - name: node-js_api
   243      properties:
   244        url: ${default-url}
   245  `))
   246  			Ω(e).Should(Succeed())
   247  			Ω(actual).Should(Equal(expected))
   248  			validateMtaArchiveContents([]string{"node/", "node/data.zip", "node-js/", "node-js/data.zip"}, filepath.Join(path, "mta_archives", "mta_demo_0.0.1.mtar"))
   249  		})
   250  		//It("Generate MTAR for mta_java", func() {
   251  		//
   252  		//	dir, _ := os.Getwd()
   253  		//	path := dir + filepath.FromSlash("/testdata/mta_java")
   254  		//	bin := filepath.FromSlash("make")
   255  		//	_, errOut, err, _ := execute(bin, "-f Makefile.mta p=cf", path)
   256  		//				Ω(err).Should(Succeed(), errOut)
   257  		//				// Check the archive was generated
   258  		//				mtarFilename := filepath.Join(dir, "testdata", "mta_java", "mta_archives", javaArchiveName)
   259  		//				Ω(filepath.Join(dir, "testdata", "mta_java", "mta_archives", javaArchiveName)).Should(BeAnExistingFile())
   260  		//				// check that module with unsupported platform 'cf' is presented in mtad.yaml
   261  		//				mtadContent, e := getFileContentFromZip(mtarFilename, "mtad.yaml")
   262  		//				Ω(e).Should(Succeed())
   263  		//				actual, e := mta.Unmarshal(mtadContent)
   264  		//				Ω(e).Should(Succeed())
   265  		//				expected, e := mta.Unmarshal([]byte(`
   266  		//	_schema-version: 2.0.0
   267  		//	ID: com.fetcher.project
   268  		//	version: 0.0.1
   269  		//	modules:
   270  		//	- name: myModule
   271  		//	 type: java.tomcat
   272  		//	 path: myModule
   273  		//	 requires:
   274  		//	 - name: otracker-uaa
   275  		//	 - name: otracker-managed-hdi
   276  		//	 parameters:
   277  		//	   buildpack: sap_java_buildpack
   278  		//	   stack: cflinuxfs3
   279  		//	resources:
   280  		//	- name: otracker-uaa
   281  		//	 type: com.sap.xs.uaa-space
   282  		//	 parameters:
   283  		//	   config-path: xs-security.json
   284  		//	- name: otracker-managed-hdi
   285  		//	 type: com.sap.xs.managed-hdi-container
   286  		//	`))
   287  		//				Ω(e).Should(Succeed())
   288  		//				Ω(actual).Should(Equal(expected))
   289  		//				validateMtaArchiveContents([]string{"myModule/", "myModule/java-xsahaa-1.1.2.war"}, filepath.Join(path, "mta_archives", "com.fetcher.project_0.0.1.mtar"))
   290  		//})
   291  
   292  		When("Running MBT commands with MTA extension descriptors", func() {
   293  			var path string
   294  			var mtarFilename string
   295  			var makefileName string
   296  			BeforeEach(func() {
   297  				dir, err := os.Getwd()
   298  				Ω(err).Should(Succeed())
   299  				path = filepath.Join(dir, "testdata", "mta_demo")
   300  				mtarFilename = filepath.Join(path, "mta_archives", demoArchiveName)
   301  				makefileName = filepath.Join(path, "Makefile.mta")
   302  			})
   303  			AfterEach(func() {
   304  				Ω(os.RemoveAll(makefileName)).Should(Succeed())
   305  				Ω(os.RemoveAll(mtarFilename)).Should(Succeed())
   306  			})
   307  
   308  			var validateMtar = func() {
   309  				// Check the MTAR was generated without the node-js module (since the extension file overrides its supported-platforms)
   310  				Ω(mtarFilename).Should(BeAnExistingFile())
   311  				validateMtaArchiveContents([]string{"node/", "node/data.zip"}, mtarFilename)
   312  
   313  				// Check the mtad.yaml has the parts from the extension file
   314  				// check that module with unsupported platform 'neo' is not present in the mtad.yaml
   315  				mtadContent, e := getFileContentFromZip(mtarFilename, "mtad.yaml")
   316  				Ω(e).Should(Succeed())
   317  				actual, e := mta.Unmarshal(mtadContent)
   318  				Ω(e).Should(Succeed())
   319  				expected, e := mta.Unmarshal([]byte(`
   320  _schema-version: "3.1"
   321  ID: mta_demo
   322  version: 0.0.1
   323  modules:
   324  - name: node
   325    type: javascript.nodejs
   326    path: node
   327    provides:
   328    - name: node_api
   329      properties:
   330        url: ${default-url}
   331  `))
   332  				Ω(e).Should(Succeed())
   333  				Ω(actual).Should(Equal(expected))
   334  			}
   335  
   336  			It("MBT build for mta_demo with extension", func() {
   337  				bin := filepath.FromSlash(binPath)
   338  				_, errOut, err := execute(bin, "build -e=ext.mtaext -p=cf", path)
   339  				Ω(err).Should(Succeed(), errOut)
   340  				validateMtar()
   341  			})
   342  
   343  			It("MBT init and run make for mta_demo with extension - non-verbose", func() {
   344  				bin := filepath.FromSlash(binPath)
   345  				cmdOut, errOut, err := execute(bin, "init -e=ext.mtaext", path)
   346  				Ω(err).Should(Succeed(), errOut)
   347  				Ω(cmdOut).ShouldNot(BeNil())
   348  				// Read the MakeFile was generated
   349  				Ω(makefileName).Should(BeAnExistingFile())
   350  				// generate mtar
   351  				_, errOut, err = execute("make", "-f Makefile.mta p=cf", path)
   352  				Ω(err).Should(Succeed(), errOut)
   353  				validateMtar()
   354  			})
   355  
   356  			It("MBT init and run make for mta_demo with extension - verbose", func() {
   357  				bin := filepath.FromSlash(binPath)
   358  				cmdOut, errOut, err := execute(bin, "init -m=verbose -e=ext.mtaext", path)
   359  				Ω(err).Should(Succeed(), errOut)
   360  				Ω(cmdOut).ShouldNot(BeNil())
   361  				// Read the MakeFile was generated
   362  				Ω(makefileName).Should(BeAnExistingFile())
   363  				// generate mtar
   364  				_, errOut, err = execute("make", "-f Makefile.mta p=cf", path)
   365  				Ω(err).Should(Succeed(), errOut)
   366  				validateMtar()
   367  			})
   368  		})
   369  	})
   370  
   371  	var _ = Describe("Generate the Verbose Makefile and use it for mtar generation", func() {
   372  
   373  		It("Generate Verbose Makefile", func() {
   374  			dir, _ := os.Getwd()
   375  			Ω(os.RemoveAll(filepath.Join(dir, "testdata", "mta_demo", "Makefile.mta"))).Should(Succeed())
   376  			Ω(os.RemoveAll(filepath.Join(dir, "testdata", "mta_demo", "mta_archives", demoArchiveName))).Should(Succeed())
   377  			path := filepath.Join(dir, "testdata", "mta_demo")
   378  			bin := filepath.FromSlash(binPath)
   379  			cmdOut, errOut, err := execute(bin, "init -m=verbose", path)
   380  			Ω(err).Should(Succeed(), errOut)
   381  			Ω(cmdOut).ShouldNot(BeNil())
   382  			// Read the MakeFile was generated
   383  			Ω(filepath.Join(dir, "testdata", "mta_demo", "Makefile.mta")).Should(BeAnExistingFile())
   384  			// generate mtar
   385  			bin = filepath.FromSlash("make")
   386  			_, errOut, err = execute(bin, "-f Makefile.mta p=cf", path)
   387  			Ω(err).Should(Succeed(), errOut)
   388  			// Check the archive was generated
   389  			Ω(filepath.Join(dir, "testdata", "mta_demo", "mta_archives", demoArchiveName)).Should(BeAnExistingFile())
   390  		})
   391  
   392  		Describe("module with dependencies", func() {
   393  			dir, _ := os.Getwd()
   394  			path := filepath.Join(dir, "testdata", "moduledep")
   395  			archivePath := filepath.Join(path, "mta_archives", "f1_0.0.1.mtar")
   396  			tempZipPath := filepath.Join(path, "mta_archives", "data.zip")
   397  
   398  			AfterEach(func() {
   399  				Ω(os.RemoveAll(filepath.Join(path, "Makefile.mta"))).Should(Succeed())
   400  				Ω(os.RemoveAll(filepath.Join(path, "mta_archives"))).Should(Succeed())
   401  				Ω(os.RemoveAll(filepath.Join(path, "public", "client"))).Should(Succeed())
   402  				Ω(os.RemoveAll(filepath.Join(path, "public", "client2"))).Should(Succeed())
   403  			})
   404  
   405  			It("Generate Verbose Makefile with module dependencies", func() {
   406  				bin := filepath.FromSlash(binPath)
   407  				cmdOut, errOut, err := execute(bin, "init -m=verbose", path)
   408  				Ω(err).Should(Succeed(), errOut)
   409  				Ω(cmdOut).ShouldNot(BeNil())
   410  				// Check the MakeFile was generated
   411  				Ω(filepath.Join(path, "Makefile.mta")).Should(BeAnExistingFile())
   412  
   413  				// Generate mtar
   414  				bin = filepath.FromSlash("make")
   415  				_, errOut, err = execute(bin, "-f Makefile.mta p=cf", path)
   416  				Ω(err).Should(Succeed(), errOut)
   417  				// Check the archive was generated
   418  				Ω(archivePath).Should(BeAnExistingFile())
   419  				validateMtaArchiveContents([]string{"module_with_dep/", "module_with_dep/data.zip"}, archivePath)
   420  
   421  				// Extract data.zip and check its content
   422  				err = extractFileFromZip(archivePath, "module_with_dep/data.zip", tempZipPath)
   423  				Ω(err).Should(Succeed())
   424  				validateArchiveContents([]string{"package.json", "client/", "client/client_package.json", "client2/", "client2/client_package.json"}, tempZipPath)
   425  			})
   426  		})
   427  	})
   428  
   429  	Describe("module with dependencies", func() {
   430  		dir, _ := os.Getwd()
   431  		path := filepath.Join(dir, "testdata", "moduledep")
   432  		archivePath := filepath.Join(path, "mta_archives", "f1_0.0.1.mtar")
   433  		tempZipPath := filepath.Join(path, "mta_archives", "data.zip")
   434  
   435  		AfterEach(func() {
   436  			Ω(os.RemoveAll(filepath.Join(path, "mta_archives"))).Should(Succeed())
   437  			Ω(os.RemoveAll(filepath.Join(path, "public", "client"))).Should(Succeed())
   438  			Ω(os.RemoveAll(filepath.Join(path, "public", "client2"))).Should(Succeed())
   439  		})
   440  
   441  		DescribeTable("Build MTA with module dependencies", func(additionalBuildOpts []string) {
   442  			bin := filepath.FromSlash(binPath)
   443  			cmdOut, _, err := executeWithArgs(bin, path, append([]string{"build"}, additionalBuildOpts...)...)
   444  			Ω(err).Should(Succeed())
   445  			Ω(cmdOut).ShouldNot(BeNil())
   446  
   447  			// Check the archive was generated
   448  			Ω(archivePath).Should(BeAnExistingFile())
   449  			validateMtaArchiveContents([]string{"module_with_dep/", "module_with_dep/data.zip"}, archivePath)
   450  
   451  			// Extract data.zip and check its content
   452  			err = extractFileFromZip(archivePath, "module_with_dep/data.zip", tempZipPath)
   453  			Ω(err).Should(Succeed())
   454  			validateArchiveContents([]string{"package.json", "client/", "client/client_package.json", "client2/", "client2/client_package.json"}, tempZipPath)
   455  		},
   456  			Entry("Non-verbose build", []string{}),
   457  			Entry("Parallel verbose build", []string{"--mode=verbose"}),
   458  			Entry("Serial verbose build", []string{"--mode=verbose", "--jobs=1"}),
   459  		)
   460  	})
   461  
   462  	var _ = Describe("MBT gen commands", func() {
   463  		It("Generate mtad", func() {
   464  			dir, _ := os.Getwd()
   465  			path := filepath.Join(dir, "testdata", "mta_demo")
   466  			Ω(os.MkdirAll(filepath.Join(path, ".mta_demo_mta_build_tmp", "node"), os.ModePerm)).Should(Succeed())
   467  			Ω(os.MkdirAll(filepath.Join(path, ".mta_demo_mta_build_tmp", "node-js"), os.ModePerm)).Should(Succeed())
   468  			bin := filepath.FromSlash(binPath)
   469  			_, errOut, err := execute(bin, "mtad-gen", path)
   470  			Ω(err).Should(Succeed(), errOut)
   471  			mtadPath := filepath.Join(path, "mtad.yaml")
   472  			Ω(mtadPath).Should(BeAnExistingFile())
   473  			content, _ := ioutil.ReadFile(mtadPath)
   474  			mtadObj, _ := mta.Unmarshal(content)
   475  			Ω(len(mtadObj.Modules)).Should(Equal(2))
   476  			Ω(mtadObj.Modules[0].Type).Should(Equal("javascript.nodejs"))
   477  			Ω(mtadObj.Modules[1].Type).Should(Equal("javascript.nodejs"))
   478  		})
   479  
   480  		It("Generate mtad with mta extension", func() {
   481  			dir, _ := os.Getwd()
   482  			path := filepath.Join(dir, "testdata", "mta_demo")
   483  			Ω(os.MkdirAll(filepath.Join(path, ".mta_demo_mta_build_tmp", "node"), os.ModePerm)).Should(Succeed())
   484  			Ω(os.MkdirAll(filepath.Join(path, ".mta_demo_mta_build_tmp", "node-js"), os.ModePerm)).Should(Succeed())
   485  			bin := filepath.FromSlash(binPath)
   486  			_, errOut, err := execute(bin, `mtad-gen -e="ext.mtaext"`, path)
   487  			Ω(err).Should(Succeed(), errOut)
   488  			mtadPath := filepath.Join(path, "mtad.yaml")
   489  			Ω(mtadPath).Should(BeAnExistingFile())
   490  			content, _ := ioutil.ReadFile(mtadPath)
   491  			mtadObj, _ := mta.Unmarshal(content)
   492  			Ω(len(mtadObj.Modules)).Should(Equal(1))
   493  			Ω(mtadObj.Modules[0].Name).Should(Equal("node"))
   494  			Ω(mtadObj.Modules[0].Type).Should(Equal("javascript.nodejs"))
   495  		})
   496  	})
   497  
   498  	var _ = Describe("Deploy basic mta archive", func() {
   499  		AfterEach(func() {
   500  			resourceCleanup("node")
   501  			resourceCleanup("node-js")
   502  		})
   503  		It("Deploy MTAR", func() {
   504  			dir, _ := os.Getwd()
   505  			path := dir + filepath.FromSlash("/testdata/mta_demo/mta_archives")
   506  			bin := filepath.FromSlash("cf")
   507  			// Execute deployment process with output to make the deployment success/failure more clear
   508  			err := executeWithOutput(bin, "deploy "+demoArchiveName+" -f", path)
   509  			Ω(err).Should(Succeed())
   510  			// Check if the deploy succeeded by using curl command response.
   511  			// Receiving the output status code 200 represents successful deployment
   512  			args := "-s -o /dev/null -w '%{http_code}' " + os.Getenv("NODE_APP_ROUTE")
   513  			path = dir + filepath.FromSlash("/testdata/mta_demo")
   514  			bin = filepath.FromSlash("curl")
   515  			cmdOut, errOut, err := executeEverySecond(bin, args, path)
   516  			if len(errOut) > 0 {
   517  				log.Println(errOut)
   518  			}
   519  			Ω(err).Should(Succeed())
   520  			Ω(cmdOut).Should(Equal("'200'"))
   521  		})
   522  	})
   523  
   524  	var _ = Describe("Generate merged mta.yaml", func() {
   525  		AfterEach(func() {
   526  			dir, _ := os.Getwd()
   527  			Ω(os.RemoveAll(filepath.Join(dir, "testdata", "mta_demo", "result.yaml"))).Should(Succeed())
   528  		})
   529  		It("merges with one extension", func() {
   530  			dir, _ := os.Getwd()
   531  			path := filepath.Join(dir, "testdata", "mta_demo")
   532  			bin := filepath.FromSlash(binPath)
   533  			_, errOut, err := execute(bin, `merge -e=ext.mtaext -n=result.yaml`, path)
   534  			Ω(err).Should(Succeed(), errOut)
   535  			mtadPath := filepath.Join(path, "result.yaml")
   536  			Ω(mtadPath).Should(BeAnExistingFile())
   537  			content, _ := ioutil.ReadFile(mtadPath)
   538  			mtaObj, _ := mta.Unmarshal(content)
   539  			expected, e := mta.Unmarshal([]byte(`
   540  ID: mta_demo
   541  _schema-version: '2.1'
   542  version: 0.0.1
   543  
   544  modules:
   545  - name: node
   546    type: nodejs
   547    path: node
   548    provides:
   549    - name: node_api
   550      properties:
   551        url: ${default-url}
   552    build-parameters:
   553      supported-platforms: [cf]
   554  - name: node-js
   555    type: nodejs
   556    path: node-js
   557    provides:
   558    - name: node-js_api
   559      properties:
   560        url: ${default-url}
   561    build-parameters:
   562      builder: zip
   563      supported-platforms: [neo]
   564  `))
   565  			Ω(e).Should(Succeed())
   566  			Ω(mtaObj).Should(Equal(expected))
   567  		})
   568  	})
   569  
   570  	var _ = Describe("Assemble MTAR", func() {
   571  		var currentWorkingDirectory string
   572  		var mtaAssemblePath string
   573  		var resultMtarPath string
   574  
   575  		BeforeEach(func() {
   576  			currentWorkingDirectory, _ = os.Getwd()
   577  			mtaAssemblePath = currentWorkingDirectory + filepath.FromSlash("/testdata/mta_assemble")
   578  			resultMtarPath = filepath.Join(mtaAssemblePath, "mta_archives", "mta.assembly.example_1.3.3.mtar")
   579  		})
   580  
   581  		AfterEach(func() {
   582  			Ω(os.RemoveAll(filepath.Join(mtaAssemblePath, "mta.assembly.example.mtar"))).Should(Succeed())
   583  			Ω(os.Chdir(currentWorkingDirectory)).Should(Succeed())
   584  			Ω(os.RemoveAll(filepath.FromSlash("./testdata/mta_assemble/mta_archives"))).Should(Succeed())
   585  		})
   586  
   587  		It("Assemble MTAR", func() {
   588  			bin := filepath.FromSlash(binPath)
   589  			cmdOut, errOut, err := execute(bin, "assemble", mtaAssemblePath)
   590  			Ω(err).Should(Succeed(), errOut)
   591  			Ω(cmdOut).ShouldNot(BeNil())
   592  			Ω(cmdOut).Should(ContainSubstring("assembling the MTA project..." + "\n"))
   593  			Ω(cmdOut).Should(ContainSubstring("copying the MTA content..." + "\n"))
   594  			Ω(cmdOut).Should(ContainSubstring("generating the metadata..." + "\n"))
   595  			Ω(cmdOut).Should(ContainSubstring("generating the MTA archive..." + "\n"))
   596  			Ω(cmdOut).Should(ContainSubstring("the MTA archive generated at: " + resultMtarPath + "\n"))
   597  			Ω(cmdOut).Should(ContainSubstring("cleaning temporary files..." + "\n"))
   598  			Ω(resultMtarPath).Should(BeAnExistingFile())
   599  			validateMtaArchiveContents([]string{
   600  				"node.zip", "xs-security.json",
   601  				"node/", "node/.eslintrc", "node/.eslintrc.ext", "node/.gitignore", "node/.npmrc", "node/jest.json", "node/package.json", "node/runTest.js", "node/server.js",
   602  				"node/.che/", "node/.che/project.json",
   603  				"node/tests/", "node/tests/sample-spec.js",
   604  			}, resultMtarPath)
   605  		})
   606  
   607  		It("Assemble MTAR with MTA extension", func() {
   608  			bin := filepath.FromSlash(binPath)
   609  			cmdOut, errOut, err := execute(bin, fmt.Sprintf(`assemble -e="my.mtaext"`), mtaAssemblePath)
   610  			Ω(err).Should(Succeed(), errOut)
   611  			Ω(cmdOut).ShouldNot(Equal(""))
   612  			Ω(resultMtarPath).Should(BeAnExistingFile())
   613  			// TODO the assemble command copies the contents of excluded modules to the archive (unrelated to the extension files) even though
   614  			// the modules are removed from the mtad.yaml and manifest.mf
   615  			validateMtaArchiveContents([]string{
   616  				"node.zip", "xs-security.json",
   617  				"node/", "node/.eslintrc", "node/.eslintrc.ext", "node/.gitignore", "node/.npmrc", "node/jest.json", "node/package.json", "node/runTest.js", "node/server.js",
   618  				"node/.che/", "node/.che/project.json",
   619  				"node/tests/", "node/tests/sample-spec.js",
   620  			}, resultMtarPath)
   621  			mtadContent, e := getFileContentFromZip(resultMtarPath, "mtad.yaml")
   622  			Ω(e).Should(Succeed())
   623  			actual, e := mta.Unmarshal(mtadContent)
   624  			Ω(e).Should(Succeed())
   625  			expected, e := mta.Unmarshal([]byte(`
   626  _schema-version: "3"
   627  ID: mta.assembly.example
   628  version: 1.3.3
   629  modules:
   630    - name: example2
   631      type: javascript.nodejs
   632      path: node.zip
   633      provides:
   634        - name: backend
   635          properties:
   636            url: "${default-url}"
   637      requires:
   638        - name: assembly-uaa
   639  resources:
   640    - name: mta-assembly-uaa
   641      type: org.cloudfoundry.managed-service
   642      parameters:
   643        service: xsuaa
   644        service-plan: space
   645        path: xs-security.json
   646  
   647  `))
   648  			Ω(e).Should(Succeed())
   649  			Ω(actual).Should(Equal(expected))
   650  		})
   651  	})
   652  })
   653  
   654  func buildAndInstallMBT() error {
   655  	if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
   656  		mbtName = mbtName
   657  	} else {
   658  		mbtName = mbtName + ".exe"
   659  	}
   660  	mbtTargetPath = filepath.Join(os.Getenv("GOPATH"), "/bin/"+mbtName)
   661  
   662  	cmd := exec.Command("go", "build", "-o", mbtTargetPath, ".")
   663  	cmd.Dir = filepath.FromSlash("../")
   664  	err := cmd.Run()
   665  	if err != nil {
   666  		fmt.Println("mbt build and install failed: ", err)
   667  		return err
   668  	}
   669  
   670  	return nil
   671  }
   672  
   673  func smokeTestMBT() error {
   674  	var stdout bytes.Buffer
   675  	cmd := exec.Command(mbtName, "-h")
   676  	cmd.Stdout = &stdout
   677  	err := cmd.Run()
   678  	if err != nil {
   679  		fmt.Println("exec mbt -h error: ", err)
   680  		return err
   681  	}
   682  	fmt.Println("exec mbt -h success: ", stdout.String())
   683  	return nil
   684  }
   685  
   686  func getFileContentFromZip(path string, filename string) ([]byte, error) {
   687  	zipFile, err := zip.OpenReader(path)
   688  	if err != nil {
   689  		return nil, err
   690  	}
   691  	defer zipFile.Close()
   692  	for _, file := range zipFile.File {
   693  		if strings.Contains(file.Name, filename) {
   694  			fc, err := file.Open()
   695  			if err != nil {
   696  				return nil, err
   697  			}
   698  			defer fc.Close() // If we got here there won't be another loop iteration
   699  			return ioutil.ReadAll(fc)
   700  		}
   701  	}
   702  	return nil, fmt.Errorf(`file "%s" not found`, filename)
   703  }
   704  
   705  func extractFileFromZip(archivePath string, filename string, dst string) error {
   706  	zipFile, err := zip.OpenReader(archivePath)
   707  	if err != nil {
   708  		return err
   709  	}
   710  	defer func() {
   711  		_ = zipFile.Close()
   712  	}()
   713  	var fileToExtract *zip.File = nil
   714  	for _, file := range zipFile.File {
   715  		if strings.Contains(file.Name, filename) {
   716  			fileToExtract = file
   717  		}
   718  	}
   719  	if fileToExtract == nil {
   720  		return fmt.Errorf(`file "%s" not found`, filename)
   721  	}
   722  
   723  	in, err := fileToExtract.Open()
   724  	if err != nil {
   725  		return err
   726  	}
   727  	defer func() {
   728  		_ = in.Close()
   729  	}()
   730  	err = dir.WriteFile(in, dst)
   731  	return err
   732  }
   733  
   734  func validateMtaArchiveContents(expectedAdditionalFilesInArchive []string, archiveLocation string) {
   735  	expectedFilesInArchive := append(expectedAdditionalFilesInArchive, "META-INF/", "META-INF/MANIFEST.MF", "META-INF/mtad.yaml")
   736  	validateArchiveContents(expectedFilesInArchive, archiveLocation)
   737  }
   738  
   739  func validateArchiveContents(expectedFilesInArchive []string, archiveLocation string) {
   740  	archive, err := zip.OpenReader(archiveLocation)
   741  	Ω(err).Should(Succeed())
   742  	defer func() {
   743  		_ = archive.Close()
   744  	}()
   745  	var filesInArchive []string
   746  	for _, file := range archive.File {
   747  		filesInArchive = append(filesInArchive, file.Name)
   748  	}
   749  	for _, expectedFile := range expectedFilesInArchive {
   750  		Ω(contains(expectedFile, filesInArchive)).Should(BeTrue(), fmt.Sprintf("expected %s to be in the archive; archive contains %v", expectedFile, filesInArchive))
   751  	}
   752  	for _, existingFile := range filesInArchive {
   753  		Ω(contains(existingFile, expectedFilesInArchive)).Should(BeTrue(), fmt.Sprintf("did not expect %s to be in the archive; archive contains %v", existingFile, filesInArchive))
   754  	}
   755  }
   756  
   757  func contains(element string, elements []string) bool {
   758  	for _, el := range elements {
   759  		if el == element {
   760  			return true
   761  		}
   762  	}
   763  	return false
   764  }
   765  
   766  // execute with live output
   767  func executeWithOutput(bin string, args string, path string) error {
   768  	cmd := exec.Command(bin, strings.Split(args, " ")...)
   769  	cmd.Dir = path
   770  	cmdReader, err := cmd.StdoutPipe()
   771  	if err != nil {
   772  		return errors.Wrapf(err, "Error creating StdoutPipe for Cmd")
   773  	}
   774  	cmdErrReader, err := cmd.StderrPipe()
   775  	if err != nil {
   776  		return errors.Wrapf(err, "Error creating StderrPipe for Cmd")
   777  	}
   778  
   779  	scanner := bufio.NewScanner(cmdReader)
   780  	scannerErr := bufio.NewScanner(cmdErrReader)
   781  
   782  	go func() {
   783  		for scanner.Scan() {
   784  			fmt.Printf("process output | %s\n", scanner.Text())
   785  		}
   786  	}()
   787  	go func() {
   788  		for scannerErr.Scan() {
   789  			fmt.Printf("process error output | %s\n", scannerErr.Text())
   790  		}
   791  	}()
   792  
   793  	err = cmd.Start()
   794  	if err != nil {
   795  		return errors.Wrapf(err, "Error starting Cmd")
   796  	}
   797  	err = cmd.Wait()
   798  	if err != nil {
   799  		return errors.Wrapf(err, "Error waiting for Cmd")
   800  	}
   801  	return nil
   802  }
   803  
   804  // Delete deployed app
   805  func resourceCleanup(appName string) {
   806  	wd, _ := os.Getwd()
   807  	path := wd + filepath.FromSlash("/testdata/mta_demo")
   808  	bin := filepath.FromSlash("cf")
   809  	cmdOut, cmdErr, err := execute(bin, "delete "+appName+" -r -f", path)
   810  	Ω(err).Should(Succeed(), cmdErr)
   811  	Ω(cmdOut).ShouldNot(BeEmpty())
   812  }
   813  
   814  // Execute command every second for 40 times
   815  func executeEverySecond(bin string, args string, path string) (string, errorOut string, err error) {
   816  	n := 0
   817  	cmdOut, errOut, err := execute(bin, args, path)
   818  	for range time.Tick(time.Second) {
   819  		if n == 40 || strings.Compare(cmdOut, "'200'") == 0 {
   820  			break
   821  		}
   822  		cmdOut, errOut, err = execute(bin, args, path)
   823  		n++
   824  	}
   825  	return cmdOut, errOut, err
   826  }
   827  
   828  // Execute commands and get outputs
   829  func execute(bin string, args string, path string) (output string, errorOutput string, err error) {
   830  	return executeWithArgs(bin, path, strings.Split(args, " ")...)
   831  }
   832  
   833  func executeWithArgs(bin string, path string, args ...string) (output string, errorOutput string, err error) {
   834  	// Provide list of commands
   835  	cmd := exec.Command(bin, args...)
   836  	// bin path
   837  	cmd.Dir = path
   838  	// std out
   839  	stdoutBuf := &bytes.Buffer{}
   840  	cmd.Stdout = stdoutBuf
   841  	// std error
   842  	stdErrBuf := &bytes.Buffer{}
   843  	cmd.Stderr = stdErrBuf
   844  	// Start command
   845  	if err = cmd.Start(); err != nil {
   846  		return "", "", err
   847  	}
   848  	// wait to the command to finish
   849  	err = cmd.Wait()
   850  	return stdoutBuf.String(), stdErrBuf.String(), err
   851  }