From 8c112e01dd58cb7133c319ee2d7716f4c3e73c3f Mon Sep 17 00:00:00 2001 From: Zhao Xiaojie Date: Sat, 25 Apr 2020 21:23:49 +0800 Subject: [PATCH] Add integration tests (#301) * Add a simple test for plugin * Do int test in ubuntu * Run the int test when pull-request * Fix the build error caused by bindata * Fix the wrong name of int test action * Fix the path for build * Add more int test cases * Add more test cases which needs dependencies * Add more test cases * Add more test environments * Fix the issues come from crumb cmd * Fix the port conflict problem * Ignore the result of restart jenkins * Fix the panic of doc cmd * Fix the doc cmd * Given a specific version for gomock * Add missing import * Add slow testing * Remove test package to e2e * Rename package * Test fixes * Auto delete random web dir * Fix the e2e tests --- .../workflows/{backup.yaml => backup.yaml.md} | 0 .github/workflows/dev-image.yaml | 0 .github/workflows/int-test-darwin.yaml | 50 +++++++++++++ .github/workflows/int-test-ubuntu.yaml | 50 +++++++++++++ Makefile | 19 +++++ app/cmd/casc_apply.go | 2 +- app/cmd/center_start.go | 29 ++++++-- app/cmd/config_list.go | 15 ++-- app/cmd/credential_create.go | 2 +- app/cmd/crumbIssuer.go | 12 ++-- app/cmd/plugin_download.go | 20 ++++-- app/cmd/queue_list.go | 2 +- app/cmd/root.go | 22 +++++- client/common.go | 13 ++-- client/common_test.go | 5 +- client/pluginApi.go | 9 ++- e2e/common.go | 51 +++++++++++++ e2e/completion_test.go | 34 +++++++++ e2e/computer_test.go | 17 +++++ e2e/config_test.go | 27 +++++++ e2e/crumb_test.go | 14 ++++ e2e/doc_test.go | 22 ++++++ e2e/job_test.go | 18 +++++ e2e/plugin_test.go | 54 ++++++++++++++ e2e/queue_test.go | 16 +++++ e2e/root_test.go | 14 ++++ e2e/setup_test.go | 60 ++++++++++++++++ e2e/withdependencies/casc_test.go | 14 ++++ e2e/withdependencies/credential_test.go | 17 +++++ e2e/withdependencies/job_test.go | 24 +++++++ e2e/withdependencies/setup_test.go | 72 +++++++++++++++++++ go.mod | 1 + go.sum | 2 + 33 files changed, 668 insertions(+), 39 deletions(-) rename .github/workflows/{backup.yaml => backup.yaml.md} (100%) create mode 100644 .github/workflows/dev-image.yaml create mode 100644 .github/workflows/int-test-darwin.yaml create mode 100644 .github/workflows/int-test-ubuntu.yaml create mode 100644 e2e/common.go create mode 100644 e2e/completion_test.go create mode 100644 e2e/computer_test.go create mode 100644 e2e/config_test.go create mode 100644 e2e/crumb_test.go create mode 100644 e2e/doc_test.go create mode 100644 e2e/job_test.go create mode 100644 e2e/plugin_test.go create mode 100644 e2e/queue_test.go create mode 100644 e2e/root_test.go create mode 100644 e2e/setup_test.go create mode 100644 e2e/withdependencies/casc_test.go create mode 100644 e2e/withdependencies/credential_test.go create mode 100644 e2e/withdependencies/job_test.go create mode 100644 e2e/withdependencies/setup_test.go diff --git a/.github/workflows/backup.yaml b/.github/workflows/backup.yaml.md similarity index 100% rename from .github/workflows/backup.yaml rename to .github/workflows/backup.yaml.md diff --git a/.github/workflows/dev-image.yaml b/.github/workflows/dev-image.yaml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/int-test-darwin.yaml b/.github/workflows/int-test-darwin.yaml new file mode 100644 index 0000000..fc4a8f9 --- /dev/null +++ b/.github/workflows/int-test-darwin.yaml @@ -0,0 +1,50 @@ +name: IntTest-Darwin + +on: + pull_request: + branches: + - master + +jobs: + build: + name: Build + runs-on: macOS-latest + steps: + + - name: Set up Go 1.13 + uses: actions/setup-go@v1 + with: + go-version: 1.13 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + + - name: Build + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/darwin:/System/Volumes/Data/Users/runner/go/bin + make go-bindata-download-darwin tools init darwin + + - name: Test in Jenkins 2.190.1 + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/darwin:/System/Volumes/Data/Users/runner/go/bin + export JENKINS_VERSION=2.190.1 + make test-slow + + - name: Test in Jenkins 2.190.2 + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/darwin:/System/Volumes/Data/Users/runner/go/bin + export JENKINS_VERSION=2.190.2 + make test-slow + + - name: Test in Jenkins 2.190.3 + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/darwin:/System/Volumes/Data/Users/runner/go/bin + export JENKINS_VERSION=2.190.2 + make test-slow + + - name: Test in Jenkins 2.204.1 + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/darwin:/System/Volumes/Data/Users/runner/go/bin + export JENKINS_VERSION=2.204.1 + make test-slow \ No newline at end of file diff --git a/.github/workflows/int-test-ubuntu.yaml b/.github/workflows/int-test-ubuntu.yaml new file mode 100644 index 0000000..eb69b6d --- /dev/null +++ b/.github/workflows/int-test-ubuntu.yaml @@ -0,0 +1,50 @@ +name: IntTest-Ubuntu + +on: + pull_request: + branches: + - master + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + + - name: Set up Go 1.13 + uses: actions/setup-go@v1 + with: + go-version: 1.13 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + + - name: Build + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/linux:/System/Volumes/Data/Users/runner/go/bin + make go-bindata-download-linux tools init linux + + - name: Test in Jenkins 2.190.1 + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/linux:/System/Volumes/Data/Users/runner/go/bin + export JENKINS_VERSION=2.190.1 + make test-slow + + - name: Test in Jenkins 2.190.2 + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/linux:/System/Volumes/Data/Users/runner/go/bin + export JENKINS_VERSION=2.190.2 + make test-slow + + - name: Test in Jenkins 2.190.3 + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/linux:/System/Volumes/Data/Users/runner/go/bin + export JENKINS_VERSION=2.190.2 + make test-slow + + - name: Test in Jenkins 2.204.1 + run: | + export PATH=$PATH:$GOPATH/bin:/home/runner/go/bin:${PWD}/bin:${PWD}/bin/linux:/System/Volumes/Data/Users/runner/go/bin + export JENKINS_VERSION=2.204.1 + make test-slow \ No newline at end of file diff --git a/Makefile b/Makefile index 34124b3..38aad12 100644 --- a/Makefile +++ b/Makefile @@ -95,6 +95,25 @@ fmt: go fmt ./app/... gofmt -s -w . +test-slow: +# JENKINS_VERSION=2.190.3 go test ./e2e/... -v -count=1 -parallel 1 + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestBashCompletion$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestZshCompletion$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestPowerShellCompletion$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestListComputers$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestConfigList$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestConfigGenerate$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestConfigList$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestShowCurrentConfig$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestCrumb$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestDoc$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestSearchPlugins$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestListPlugins$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestCheckUpdateCenter$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestInstallPlugin$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestDownloadPlugin$ + JENKINS_VERSION=2.190.3 go test github.com/jenkins-zh/jenkins-cli/e2e -v -test.run ^TestListQueue$ + test: mkdir -p bin go test ./util -v -count=1 diff --git a/app/cmd/casc_apply.go b/app/cmd/casc_apply.go index 30c1fa6..a4bc0c9 100644 --- a/app/cmd/casc_apply.go +++ b/app/cmd/casc_apply.go @@ -32,7 +32,7 @@ var cascApplyCmd = &cobra.Command{ RoundTripper: cascApplyOption.RoundTripper, }, } - getCurrentJenkinsAndClientOrDie(&(jClient.JenkinsCore)) + getCurrentJenkinsAndClient(&(jClient.JenkinsCore)) return jClient.Apply() }, Annotations: map[string]string{ diff --git a/app/cmd/center_start.go b/app/cmd/center_start.go index aeb57ee..5aef557 100644 --- a/app/cmd/center_start.go +++ b/app/cmd/center_start.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "github.com/jenkins-zh/jenkins-cli/app/cmd/common" + "github.com/jenkins-zh/jenkins-cli/app/helper" "go.uber.org/zap" "os" "path/filepath" @@ -36,10 +37,11 @@ type CenterStartOption struct { Environments []string System []string - Download bool - Version string - LTS bool - Formula string + Download bool + Version string + LTS bool + Formula string + RandomWebDir bool DryRun bool } @@ -82,6 +84,8 @@ func init() { centerStartCmd.Flags().IntVarP(¢erStartOption.ConcurrentIndexing, "concurrent-indexing", "", -1, i18n.T("Concurrent indexing limit, take this value only it is bigger than -1")) + centerStartCmd.Flags().BoolVarP(¢erStartOption.RandomWebDir, "random-web-dir", "", false, + i18n.T("If start jenkins.war in a random web dir")) centerStartCmd.Flags().BoolVarP(¢erStartOption.DryRun, "dry-run", "", false, i18n.T("Don't run jenkins.war really")) @@ -145,7 +149,19 @@ var centerStartCmd = &cobra.Command{ binary, err = util.LookPath("java", centerStartOption.LookPathContext) if err == nil { env := os.Environ() - env = append(env, fmt.Sprintf("JENKINS_HOME=%s/.jenkins-cli/cache/%s/web", userHome, centerStartOption.Version)) + + if centerStartOption.RandomWebDir { + randomWebDir := fmt.Sprintf("JENKINS_HOME=%s/.jenkins-cli/cache/%s/web", os.TempDir(), centerStartOption.Version) + defer func(logger helper.Printer, randomWebDir string) { + if err := os.RemoveAll(randomWebDir); err != nil { + logger.PrintErr(fmt.Sprintf("remove random web dir [%s] of Jenkins failed, %#v", randomWebDir, err)) + } + }(cmd, randomWebDir) + + env = append(env, randomWebDir) + } else { + env = append(env, fmt.Sprintf("JENKINS_HOME=%s/.jenkins-cli/cache/%s/web", userHome, centerStartOption.Version)) + } if centerStartOption.Environments != nil { for _, item := range centerStartOption.Environments { @@ -157,7 +173,8 @@ var centerStartCmd = &cobra.Command{ jenkinsWarArgs = centerStartOption.setSystemProperty(jenkinsWarArgs) jenkinsWarArgs = append(jenkinsWarArgs, "-jar", jenkinsWar) jenkinsWarArgs = append(jenkinsWarArgs, fmt.Sprintf("--httpPort=%d", centerStartOption.Port)) - jenkinsWarArgs = append(jenkinsWarArgs, "--argumentsRealm.passwd.admin=admin --argumentsRealm.roles.admin=admin") + jenkinsWarArgs = append(jenkinsWarArgs, "--argumentsRealm.passwd.admin=admin") + jenkinsWarArgs = append(jenkinsWarArgs, "--argumentsRealm.roles.admin=admin") jenkinsWarArgs = append(jenkinsWarArgs, fmt.Sprintf("--prefix=%s", centerStartOption.Context)) if centerStartOption.HTTPSEnable { diff --git a/app/cmd/config_list.go b/app/cmd/config_list.go index 2305e06..0e6ccf8 100644 --- a/app/cmd/config_list.go +++ b/app/cmd/config_list.go @@ -31,21 +31,26 @@ var configListCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, _ []string) (err error) { configListOption.Writer = cmd.OutOrStdout() + config := getConfig() + if config == nil { + return fmt.Errorf("no config file found") + } + switch configListOption.Config { case "JenkinsServers": - err = configListOption.OutputV2(getConfig().JenkinsServers) + err = configListOption.OutputV2(config.JenkinsServers) case "PreHooks": configListOption.Columns = "Path,Command" - err = configListOption.OutputV2(getConfig().PreHooks) + err = configListOption.OutputV2(config.PreHooks) case "PostHooks": configListOption.Columns = "Path,Command" - err = configListOption.OutputV2(getConfig().PostHooks) + err = configListOption.OutputV2(config.PostHooks) case "Mirrors": configListOption.Columns = "Name,URL" - err = configListOption.OutputV2(getConfig().Mirrors) + err = configListOption.OutputV2(config.Mirrors) case "PluginSuites": configListOption.Columns = "Name,Description" - err = configListOption.OutputV2(getConfig().PluginSuites) + err = configListOption.OutputV2(config.PluginSuites) default: err = fmt.Errorf("unknow config %s", configListOption.Config) } diff --git a/app/cmd/credential_create.go b/app/cmd/credential_create.go index ecacc95..56376c6 100644 --- a/app/cmd/credential_create.go +++ b/app/cmd/credential_create.go @@ -50,7 +50,7 @@ func init() { } var credentialCreateCmd = &cobra.Command{ - Use: "create [store] [id]", + Use: "create", Short: i18n.T("Create a credential from Jenkins"), Long: i18n.T("Create a credential from Jenkins"), PreRunE: func(cmd *cobra.Command, args []string) (err error) { diff --git a/app/cmd/crumbIssuer.go b/app/cmd/crumbIssuer.go index 70e16ca..dafbdac 100644 --- a/app/cmd/crumbIssuer.go +++ b/app/cmd/crumbIssuer.go @@ -3,8 +3,6 @@ package cmd import ( "net/http" - "github.com/jenkins-zh/jenkins-cli/app/helper" - "github.com/jenkins-zh/jenkins-cli/client" "github.com/spf13/cobra" @@ -25,14 +23,14 @@ var crumbIssuerCmd = &cobra.Command{ Use: "crumb", Short: "Print crumbIssuer of Jenkins", Long: `Print crumbIssuer of Jenkins`, - Run: func(cmd *cobra.Command, _ []string) { + RunE: func(cmd *cobra.Command, _ []string) (err error) { jenkinsCore := &client.JenkinsCore{RoundTripper: crumbIssuerOptions.RoundTripper} - getCurrentJenkinsAndClientOrDie(jenkinsCore) + getCurrentJenkinsAndClient(jenkinsCore) - crumb, err := jenkinsCore.GetCrumb() - if err == nil { + var crumb *client.JenkinsCrumb + if crumb, err = jenkinsCore.GetCrumb(); err == nil { cmd.Printf("%s=%s\n", crumb.CrumbRequestField, crumb.Crumb) } - helper.CheckErr(cmd, err) + return }, } diff --git a/app/cmd/plugin_download.go b/app/cmd/plugin_download.go index 5e66408..1406667 100644 --- a/app/cmd/plugin_download.go +++ b/app/cmd/plugin_download.go @@ -1,10 +1,11 @@ package cmd import ( + "net/http" + "github.com/jenkins-zh/jenkins-cli/app/i18n" "github.com/jenkins-zh/jenkins-cli/client" "github.com/spf13/cobra" - "net/http" ) // PluginDownloadOption is the option for plugin download command @@ -13,6 +14,7 @@ type PluginDownloadOption struct { SkipOptional bool UseMirror bool ShowProgress bool + DownloadDir string RoundTripper http.RoundTripper } @@ -29,22 +31,26 @@ func init() { i18n.T("If you want to download plugin from a mirror site")) pluginDownloadCmd.Flags().BoolVarP(&pluginDownloadOption.ShowProgress, "show-progress", "", true, i18n.T("If you want to show the progress of download a plugin")) + pluginDownloadCmd.Flags().StringVarP(&pluginDownloadOption.DownloadDir, "download-dir", "", "", + i18n.T("The directory which you want to download to")) } var pluginDownloadCmd = &cobra.Command{ - Use: "download ", - Short: i18n.T("Download the plugins"), - Long: i18n.T(`Download the plugins which contain the target plugin and its dependencies`), - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { + Use: "download", + Short: i18n.T("Download the plugins"), + Long: i18n.T(`Download the plugins which contain the target plugin and its dependencies`), + Args: cobra.MinimumNArgs(1), + Example: "download localization-zh-cn", + RunE: func(cmd *cobra.Command, args []string) (err error) { jClient := &client.PluginAPI{ SkipDependency: pluginDownloadOption.SkipDependency, SkipOptional: pluginDownloadOption.SkipOptional, UseMirror: pluginDownloadOption.UseMirror, ShowProgress: pluginDownloadOption.ShowProgress, MirrorURL: getDefaultMirror(), + DownloadDir: pluginDownloadOption.DownloadDir, RoundTripper: pluginDownloadOption.RoundTripper, } - jClient.DownloadPlugins(args) + return jClient.DownloadPlugins(args) }, } diff --git a/app/cmd/queue_list.go b/app/cmd/queue_list.go index a2d26d6..ee3b392 100644 --- a/app/cmd/queue_list.go +++ b/app/cmd/queue_list.go @@ -34,7 +34,7 @@ var queueListCmd = &cobra.Command{ Debug: rootOptions.Debug, }, } - getCurrentJenkinsAndClientOrDie(&(jClient.JenkinsCore)) + getCurrentJenkinsAndClient(&(jClient.JenkinsCore)) var jobQueue *client.JobQueue if jobQueue, err = jClient.Get(); err == nil { diff --git a/app/cmd/root.go b/app/cmd/root.go index fbbea18..3632987 100644 --- a/app/cmd/root.go +++ b/app/cmd/root.go @@ -26,6 +26,7 @@ var logger *zap.Logger // RootOptions is a global option for whole cli type RootOptions struct { ConfigFile string + ConfigLoad bool Jenkins string Debug bool @@ -35,6 +36,7 @@ type RootOptions struct { InsecureSkipVerify bool Proxy string ProxyAuth string + ProxyDisable bool Doctor bool @@ -59,7 +61,8 @@ We'd love to hear your feedback at https://github.com/jenkins-zh/jenkins-cli/iss return } - if needReadConfig(cmd) { + rootOptions.ConfigLoad = !("false" == os.Getenv("JCLI_CONFIG_LOAD")) + if rootOptions.ConfigLoad && needReadConfig(cmd) { if rootOptions.ConfigFile == "" { rootOptions.ConfigFile = os.Getenv("JCLI_CONFIG") } @@ -144,6 +147,8 @@ var rootOptions RootOptions func init() { rootCmd.PersistentFlags().StringVarP(&rootOptions.ConfigFile, "configFile", "", "", i18n.T("An alternative config file")) + rootCmd.PersistentFlags().BoolVarP(&rootOptions.ConfigLoad, "config-load", "", true, + i18n.T("If load a default config file")) rootCmd.PersistentFlags().StringVarP(&rootOptions.Jenkins, "jenkins", "j", "", i18n.T("Select a Jenkins server for this time")) rootCmd.PersistentFlags().BoolVarP(&rootOptions.Debug, "debug", "", false, "Print the output into debug.html") @@ -164,6 +169,8 @@ func init() { i18n.T("The proxy of connection to Jenkins")) rootCmd.PersistentFlags().StringVarP(&rootOptions.ProxyAuth, "proxy-auth", "", "", i18n.T("The auth of proxy of connection to Jenkins")) + rootCmd.PersistentFlags().BoolVarP(&rootOptions.ProxyDisable, "proxy-disable", "", false, + i18n.T("Disable proxy setting")) rootCmd.SetOut(os.Stdout) @@ -196,6 +203,19 @@ func getCurrentJenkinsFromOptions() (jenkinsServer *JenkinsServer) { if rootOptions.Token != "" { jenkinsServer.Token = rootOptions.Token } + + if rootOptions.Proxy != "" { + jenkinsServer.Proxy = rootOptions.Proxy + } + + if rootOptions.ProxyAuth != "" { + jenkinsServer.ProxyAuth = rootOptions.ProxyAuth + } + + if rootOptions.ProxyDisable { + jenkinsServer.Proxy = "" + jenkinsServer.ProxyAuth = "" + } } return } diff --git a/client/common.go b/client/common.go index dd7ae74..35d6dfb 100644 --- a/client/common.go +++ b/client/common.go @@ -66,6 +66,7 @@ func (j *JenkinsCore) GetClient() (client *http.Client) { func (j *JenkinsCore) ProxyHandle(request *http.Request) { if j.ProxyAuth != "" { basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(j.ProxyAuth)) + logger.Debug("setting proxy for HTTP request", zap.String("header", basicAuth)) request.Header.Add("Proxy-Authorization", basicAuth) } } @@ -116,11 +117,11 @@ func (j *JenkinsCore) GetCrumb() (crumbIssuer *JenkinsCrumb, err error) { err = json.Unmarshal(data, &crumbIssuer) } else if statusCode == 404 { // return 404 if Jenkins does no have crumb + err = fmt.Errorf("crumb is disabled") } else { err = fmt.Errorf("unexpected status code: %d", statusCode) } } - return } @@ -222,10 +223,14 @@ func (j *JenkinsCore) RequestWithResponse(method, api string, headers map[string func (j *JenkinsCore) Request(method, api string, headers map[string]string, payload io.Reader) ( statusCode int, data []byte, err error) { var ( - req *http.Request - response *http.Response + req *http.Request + response *http.Response + requestURL string ) - if req, err = http.NewRequest(method, fmt.Sprintf("%s%s", j.URL, api), payload); err != nil { + + requestURL = fmt.Sprintf("%s%s", j.URL, api) + logger.Debug("send HTTP request", zap.String("URL", requestURL), zap.String("method", method)) + if req, err = http.NewRequest(method, requestURL, payload); err != nil { return } if language != "" { diff --git a/client/common_test.go b/client/common_test.go index 1c0a218..7e11753 100644 --- a/client/common_test.go +++ b/client/common_test.go @@ -110,9 +110,8 @@ var _ = Describe("common test", func() { roundTripper.EXPECT(). RoundTrip(requestCrumb).Return(responseCrumb, nil) - crumb, err := jenkinsCore.GetCrumb() - Expect(crumb).To(BeNil()) - Expect(err).To(BeNil()) + _, err := jenkinsCore.GetCrumb() + Expect(err).To(HaveOccurred()) }) It("with crumb setting", func() { diff --git a/client/pluginApi.go b/client/pluginApi.go index 0bb3e87..d387443 100644 --- a/client/pluginApi.go +++ b/client/pluginApi.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "log" "net/http" + "path" "strings" "github.com/jenkins-zh/jenkins-cli/util" @@ -22,6 +23,7 @@ type PluginAPI struct { UseMirror bool ShowProgress bool MirrorURL string + DownloadDir string RoundTripper http.RoundTripper } @@ -118,7 +120,7 @@ func (d *PluginAPI) ShowTrend(name string) (trend string, err error) { } // DownloadPlugins will download those plugins from update center -func (d *PluginAPI) DownloadPlugins(names []string) { +func (d *PluginAPI) DownloadPlugins(names []string) (err error) { d.dependencyMap = make(map[string]string) logger.Info("start to collect plugin dependencies...") plugins := make([]PluginInfo, 0) @@ -128,7 +130,6 @@ func (d *PluginAPI) DownloadPlugins(names []string) { } logger.Info("ready to download plugins", zap.Int("total", len(plugins))) - var err error for i, plugin := range plugins { logger.Info("start to download plugin", zap.String("name", plugin.Name), @@ -138,8 +139,10 @@ func (d *PluginAPI) DownloadPlugins(names []string) { if err = d.download(plugin.URL, plugin.Name); err != nil { logger.Error("download plugin error", zap.String("name", plugin.Name), zap.Error(err)) + break } } + return } func (d *PluginAPI) getMirrorURL(url string) (mirror string) { @@ -157,7 +160,7 @@ func (d *PluginAPI) download(url string, name string) (err error) { downloader := util.HTTPDownloader{ RoundTripper: d.RoundTripper, - TargetFilePath: fmt.Sprintf("%s.hpi", name), + TargetFilePath: path.Join(d.DownloadDir, fmt.Sprintf("%s.hpi", name)), URL: url, ShowProgress: d.ShowProgress, } diff --git a/e2e/common.go b/e2e/common.go new file mode 100644 index 0000000..9b11868 --- /dev/null +++ b/e2e/common.go @@ -0,0 +1,51 @@ +package e2e + +import ( + "fmt" + "io" + "os/exec" + "strings" +) + +// ExecuteCmd execute a jcli command +func ExecuteCmd(args ...string) { + if err := exec.Command("jcli", args...).Run(); err != nil { + panic(err) + } +} + +// InstallPlugin install a plugin by jcli +func InstallPlugin(name, jenkins string, wait bool) { + ExecuteCmd("plugin", "install", name, "--url", jenkins) + fmt.Printf("install %s done\n", name) + if wait { + ExecuteCmd("center", "watch", "--util-install-complete", "--url", jenkins) + } +} + +// RestartAndWait restart Jenkins then wait it +func RestartAndWait(jenkins string, outputReader io.ReadCloser) { + buf := make([]byte, 1024, 1024) + // should assert the error of restart + _ = exec.Command("jcli", "restart", "-b", "--url", jenkins).Run() + for { + if strNum, err := outputReader.Read(buf); err != nil || strings.Contains(string(buf[:strNum]), "Jenkins is fully up and running") { + break + } else { + fmt.Print(string(buf[:strNum])) + } + } +} + +// WaitRunningUp wait until Jenkins running up +func WaitRunningUp(outputReader io.ReadCloser) { + buf := make([]byte, 1024, 1024) + for { + if strNum, err := outputReader.Read(buf); err != nil || strings.Contains(string(buf[:strNum]), "Jenkins is fully up and running") { + fmt.Print(string(buf[:strNum])) + break + } else { + fmt.Print(string(buf[:strNum])) + } + } +} diff --git a/e2e/completion_test.go b/e2e/completion_test.go new file mode 100644 index 0000000..3daa69b --- /dev/null +++ b/e2e/completion_test.go @@ -0,0 +1,34 @@ +package e2e + +import ( + "github.com/stretchr/testify/assert" + "os/exec" + "testing" +) + +func TestBashCompletion(t *testing.T) { + cmd := exec.Command("jcli", "completion") + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(data), "bash completion for jcli") + + // with options + cmd = exec.Command("jcli", "completion", "--type", "bash") + data, err = cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(data), "bash completion for jcli") +} + +func TestZshCompletion(t *testing.T) { + cmd := exec.Command("jcli", "completion", "--type", "zsh") + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(data), "#compdef _jcli jcli") +} + +func TestPowerShellCompletion(t *testing.T) { + cmd := exec.Command("jcli", "completion", "--type", "powerShell") + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(data), "using namespace System.Management.Automation") +} diff --git a/e2e/computer_test.go b/e2e/computer_test.go new file mode 100644 index 0000000..e0fd60c --- /dev/null +++ b/e2e/computer_test.go @@ -0,0 +1,17 @@ +package e2e + +import ( + "fmt" + "os/exec" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListComputers(t *testing.T) { + cmd := exec.Command("jcli", "computer", "list", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} diff --git a/e2e/config_test.go b/e2e/config_test.go new file mode 100644 index 0000000..41c2749 --- /dev/null +++ b/e2e/config_test.go @@ -0,0 +1,27 @@ +package e2e + +import ( + "github.com/stretchr/testify/assert" + "os/exec" + "testing" +) + +func TestConfigList(t *testing.T) { + cmd := exec.Command("jcli", "config", "list") + _, err := cmd.CombinedOutput() + assert.NotNil(t, err) +} + +func TestConfigGenerate(t *testing.T) { + cmd := exec.Command("jcli", "config", "generate", "-i=false") + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(data), "jenkins_servers") +} + +func TestShowCurrentConfig(t *testing.T) { + cmd := exec.Command("jcli", "config") + data, err := cmd.CombinedOutput() + assert.NotNil(t, err) + assert.Contains(t, string(data), "Error: no config file found or no current setting") +} diff --git a/e2e/crumb_test.go b/e2e/crumb_test.go new file mode 100644 index 0000000..81b1d47 --- /dev/null +++ b/e2e/crumb_test.go @@ -0,0 +1,14 @@ +package e2e + +import ( + "github.com/stretchr/testify/assert" + "os/exec" + "testing" +) + +func TestCrumb(t *testing.T) { + cmd := exec.Command("jcli", "crumb", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.NotNil(t, err) + assert.Contains(t, string(data), "Error: crumb is disabled") +} diff --git a/e2e/doc_test.go b/e2e/doc_test.go new file mode 100644 index 0000000..b9561b3 --- /dev/null +++ b/e2e/doc_test.go @@ -0,0 +1,22 @@ +package e2e + +import ( + "os" + "os/exec" + "path" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDoc(t *testing.T) { + tempDir := os.TempDir() + defer os.RemoveAll(tempDir) + + cmd := exec.Command("jcli", "doc", tempDir) + _, err := cmd.CombinedOutput() + assert.Nil(t, err) + + _, err = os.Stat(path.Join(tempDir, "jcli.md")) + assert.Nil(t, err) +} diff --git a/e2e/job_test.go b/e2e/job_test.go new file mode 100644 index 0000000..d0e8f13 --- /dev/null +++ b/e2e/job_test.go @@ -0,0 +1,18 @@ +package e2e + +import ( + "fmt" + "os/exec" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListJobType(t *testing.T) { + cmd := exec.Command("jcli", "job", "type", "--url", GetJenkinsURL()) + fmt.Println(cmd.String()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} diff --git a/e2e/plugin_test.go b/e2e/plugin_test.go new file mode 100644 index 0000000..9ee4c4b --- /dev/null +++ b/e2e/plugin_test.go @@ -0,0 +1,54 @@ +package e2e + +import ( + "fmt" + "os" + "os/exec" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListPlugins(t *testing.T) { + cmd := exec.Command("jcli", "plugin", "list", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} + +func TestSearchPlugins(t *testing.T) { + cmd := exec.Command("jcli", "plugin", "search", "localization-zh-cn", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} + +func TestCheckUpdateCenter(t *testing.T) { + cmd := exec.Command("jcli", "plugin", "check", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} + +func TestInstallPlugin(t *testing.T) { + cmd := exec.Command("jcli", "plugin", "install", "localization-zh-cn", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} + +func TestDownloadPlugin(t *testing.T) { + tempDir := os.TempDir() + defer os.Remove(tempDir) + + cmd := exec.Command("jcli", "plugin", "download", "localization-zh-cn", + "--download-dir", tempDir, "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} diff --git a/e2e/queue_test.go b/e2e/queue_test.go new file mode 100644 index 0000000..a18fbaf --- /dev/null +++ b/e2e/queue_test.go @@ -0,0 +1,16 @@ +package e2e + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "os/exec" + "testing" +) + +func TestListQueue(t *testing.T) { + cmd := exec.Command("jcli", "queue", "list", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err, fmt.Sprintf("failed in cmd queue list, output is %s", string(data))) + + assert.Contains(t, string(data), "ID Why URL") +} diff --git a/e2e/root_test.go b/e2e/root_test.go new file mode 100644 index 0000000..35dac29 --- /dev/null +++ b/e2e/root_test.go @@ -0,0 +1,14 @@ +package e2e + +import ( + "github.com/stretchr/testify/assert" + "os/exec" + "testing" +) + +func TestRoot(t *testing.T) { + cmd := exec.Command("jcli") + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(data), "Jenkins CLI (jcli) manage your Jenkins") +} diff --git a/e2e/setup_test.go b/e2e/setup_test.go new file mode 100644 index 0000000..dc73166 --- /dev/null +++ b/e2e/setup_test.go @@ -0,0 +1,60 @@ +package e2e + +import ( + "fmt" + "github.com/phayes/freeport" + "io" + "os" + "os/exec" + "testing" +) + +var jenkinsURL string + +func GetJenkinsURL() string { + return jenkinsURL +} + +func TestMain(m *testing.M) { + var err error + + version := os.Getenv("JENKINS_VERSION") + os.Setenv("PATH", ".:"+os.Getenv("PATH")) + + javaHome := os.Getenv("JCLI_JAVA_HOME") + if javaHome != "" { + os.Setenv("PATH", javaHome+"/bin:"+os.Getenv("PATH")) + } + if err = os.Setenv("JCLI_CONFIG_LOAD", "false"); err != nil { + panic(err) + } + if version == "" { + return + } + + var port int + if port, err = freeport.GetFreePort(); err != nil { + fmt.Println("get free port error", err) + panic(err) + } + jenkinsURL = fmt.Sprintf("http://localhost:%d", port) + + cmd := exec.Command("jcli", "center", "start", "--random-web-dir", "--setup-wizard=false", "--port", fmt.Sprintf("%d", port), "--version", version) + fmt.Println(cmd.String()) + cmdStderrPipe, _ := cmd.StderrPipe() + if err = cmd.Start(); err != nil { + panic(err) + } + + go func(reader io.ReadCloser, cmd *exec.Cmd) { + WaitRunningUp(reader) + + m.Run() + + if err = cmd.Process.Kill(); err != nil { + panic(err) + } + }(cmdStderrPipe, cmd) + + err = cmd.Wait() +} diff --git a/e2e/withdependencies/casc_test.go b/e2e/withdependencies/casc_test.go new file mode 100644 index 0000000..46ac023 --- /dev/null +++ b/e2e/withdependencies/casc_test.go @@ -0,0 +1,14 @@ +package withdependencies + +import ( + "github.com/stretchr/testify/assert" + "os/exec" + "testing" +) + +func TestCascExport(t *testing.T) { + cmd := exec.Command("jcli", "casc", "export", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(data), "adminAddress") +} diff --git a/e2e/withdependencies/credential_test.go b/e2e/withdependencies/credential_test.go new file mode 100644 index 0000000..35c23b5 --- /dev/null +++ b/e2e/withdependencies/credential_test.go @@ -0,0 +1,17 @@ +package withdependencies + +import ( + "fmt" + "os/exec" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListCredentials(t *testing.T) { + cmd := exec.Command("jcli", "credential", "list", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} diff --git a/e2e/withdependencies/job_test.go b/e2e/withdependencies/job_test.go new file mode 100644 index 0000000..ceaac35 --- /dev/null +++ b/e2e/withdependencies/job_test.go @@ -0,0 +1,24 @@ +package withdependencies + +import ( + "fmt" + "os/exec" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSearchJobs(t *testing.T) { + cmd := exec.Command("jcli", "job", "search", "--url", GetJenkinsURL()) + data, err := cmd.CombinedOutput() + assert.Nil(t, err) + + fmt.Println(string(data)) +} + +func TestCreateJob(t *testing.T) { + cmd := exec.Command("jcli", "job", "create", "fake", + "--type", "com.cloudbees.hudson.plugins.folder.Folder", "--url", GetJenkinsURL()) + _, err := cmd.CombinedOutput() + assert.Nil(t, err) +} diff --git a/e2e/withdependencies/setup_test.go b/e2e/withdependencies/setup_test.go new file mode 100644 index 0000000..ca4570c --- /dev/null +++ b/e2e/withdependencies/setup_test.go @@ -0,0 +1,72 @@ +package withdependencies + +import ( + "fmt" + "github.com/jenkins-zh/jenkins-cli/e2e" + "github.com/phayes/freeport" + "io" + "os" + "os/exec" + "testing" +) + +var jenkinsURL string + +func GetJenkinsURL() string { + return jenkinsURL +} +func TestMain(m *testing.M) { + var err error + + version := os.Getenv("JENKINS_VERSION") + os.Setenv("PATH", ".:"+os.Getenv("PATH")) + + javaHome := os.Getenv("JCLI_JAVA_HOME") + if javaHome != "" { + os.Setenv("PATH", javaHome+"/bin:"+os.Getenv("PATH")) + } + if err = os.Setenv("JCLI_CONFIG_LOAD", "false"); err != nil { + panic(err) + } + if version == "" { + return + } + + var port int + if port, err = freeport.GetFreePort(); err != nil { + fmt.Println("get free port error", err) + panic(err) + } + jenkinsURL = fmt.Sprintf("http://localhost:%d", port) + + cmd := exec.Command("jcli", "center", "start", "--random-web-dir", "--setup-wizard=false", "--port", fmt.Sprintf("%d", port), "--version", version) + fmt.Println(cmd.String()) + cmdStderrPipe, _ := cmd.StderrPipe() + err = cmd.Start() + if err != nil { + panic(err) + } + + go func(reader io.ReadCloser, cmd *exec.Cmd) { + e2e.WaitRunningUp(reader) + + e2e.InstallPlugin("localization-zh-cn", GetJenkinsURL(), true) + + e2e.RestartAndWait(GetJenkinsURL(), reader) + + e2e.ExecuteCmd("center", "mirror", "--url", GetJenkinsURL()) + e2e.ExecuteCmd("plugin", "check", "--url", GetJenkinsURL()) + e2e.InstallPlugin("configuration-as-code", GetJenkinsURL(), true) + e2e.InstallPlugin("pipeline-restful-api", GetJenkinsURL(), true) + + e2e.RestartAndWait(GetJenkinsURL(), reader) + + m.Run() + + if err = cmd.Process.Kill(); err != nil { + panic(err) + } + }(cmdStderrPipe, cmd) + + err = cmd.Wait() +} diff --git a/go.mod b/go.mod index 37d22ba..03bac22 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/onsi/ginkgo v1.11.0 github.com/onsi/gomega v1.9.0 + github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/pkg/errors v0.8.1 github.com/spf13/cobra v1.0.0 github.com/spf13/viper v1.6.3 // indirect diff --git a/go.sum b/go.sum index 552dadf..a374d1b 100644 --- a/go.sum +++ b/go.sum @@ -157,6 +157,8 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -- GitLab