github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/command/flag/path_test.go (about)

     1  package flag_test
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"code.cloudfoundry.org/cli/cf/util/testhelpers/matchers"
    10  	. "code.cloudfoundry.org/cli/command/flag"
    11  	flags "github.com/jessevdk/go-flags"
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  )
    15  
    16  var _ = Describe("path types", func() {
    17  	type completable interface {
    18  		Complete(string) []flags.Completion
    19  	}
    20  
    21  	var (
    22  		currentDir string
    23  		tempDir    string
    24  	)
    25  
    26  	testComplete := func(subject completable) {
    27  		Describe("Complete", func() {
    28  			When("the prefix is empty", func() {
    29  				It("returns all files and directories", func() {
    30  					matches := subject.Complete("")
    31  					Expect(matches).To(ConsistOf(
    32  						flags.Completion{Item: "abc"},
    33  						flags.Completion{Item: "abd"},
    34  						flags.Completion{Item: fmt.Sprintf("~add%c", os.PathSeparator)},
    35  						flags.Completion{Item: "~abd"},
    36  						flags.Completion{Item: fmt.Sprintf("add%c", os.PathSeparator)},
    37  						flags.Completion{Item: fmt.Sprintf("aee%c", os.PathSeparator)},
    38  						flags.Completion{Item: "tfg"},
    39  						flags.Completion{Item: "ABCD"},
    40  					))
    41  				})
    42  			})
    43  
    44  			When("the prefix is not empty", func() {
    45  				When("there are matching paths", func() {
    46  					It("returns the matching paths", func() {
    47  						matches := subject.Complete("a")
    48  						Expect(matches).To(ConsistOf(
    49  							flags.Completion{Item: "abc"},
    50  							flags.Completion{Item: "abd"},
    51  							flags.Completion{Item: fmt.Sprintf("add%c", os.PathSeparator)},
    52  							flags.Completion{Item: fmt.Sprintf("aee%c", os.PathSeparator)},
    53  						))
    54  					})
    55  
    56  					It("is case sensitive", func() {
    57  						matches := subject.Complete("A")
    58  						Expect(matches).To(ConsistOf(
    59  							flags.Completion{Item: "ABCD"},
    60  						))
    61  					})
    62  
    63  					It("finds files starting with '~'", func() {
    64  						matches := subject.Complete("~")
    65  						Expect(matches).To(ConsistOf(
    66  							flags.Completion{Item: "~abd"},
    67  							flags.Completion{Item: fmt.Sprintf("~add%c", os.PathSeparator)},
    68  						))
    69  					})
    70  				})
    71  
    72  				When("there are no matching paths", func() {
    73  					It("returns no matches", func() {
    74  						Expect(subject.Complete("z")).To(BeEmpty())
    75  					})
    76  				})
    77  			})
    78  
    79  			When("the prefix is ~/", func() {
    80  				var prevHome string
    81  
    82  				BeforeEach(func() {
    83  					prevHome = os.Getenv("HOME")
    84  				})
    85  
    86  				AfterEach(func() {
    87  					os.Setenv("HOME", prevHome)
    88  				})
    89  
    90  				When("$HOME is set", func() {
    91  					var (
    92  						tempDir string
    93  						err     error
    94  					)
    95  
    96  					BeforeEach(func() {
    97  						tempDir, err = ioutil.TempDir("", "")
    98  						Expect(err).ToNot(HaveOccurred())
    99  						os.Setenv("HOME", tempDir)
   100  
   101  						for _, filename := range []string{"abc", "def"} {
   102  							err = ioutil.WriteFile(filepath.Join(tempDir, filename), []byte{}, 0400)
   103  							Expect(err).ToNot(HaveOccurred())
   104  						}
   105  
   106  						for _, dir := range []string{"adir", "bdir"} {
   107  							err = os.Mkdir(filepath.Join(tempDir, dir), 0700)
   108  							Expect(err).ToNot(HaveOccurred())
   109  						}
   110  					})
   111  
   112  					AfterEach(func() {
   113  						err = os.RemoveAll(tempDir)
   114  						Expect(err).ToNot(HaveOccurred())
   115  					})
   116  
   117  					It("returns matching paths in $HOME", func() {
   118  						matches := subject.Complete(fmt.Sprintf("~%c", os.PathSeparator))
   119  						Expect(matches).To(ConsistOf(
   120  							flags.Completion{Item: fmt.Sprintf("~%cabc", os.PathSeparator)},
   121  							flags.Completion{Item: fmt.Sprintf("~%cdef", os.PathSeparator)},
   122  							flags.Completion{Item: fmt.Sprintf("~%cadir%c", os.PathSeparator, os.PathSeparator)},
   123  							flags.Completion{Item: fmt.Sprintf("~%cbdir%c", os.PathSeparator, os.PathSeparator)},
   124  						))
   125  					})
   126  				})
   127  			})
   128  
   129  			When("the prefix starts with ~/", func() {
   130  				var prevHome string
   131  
   132  				BeforeEach(func() {
   133  					prevHome = os.Getenv("HOME")
   134  				})
   135  
   136  				AfterEach(func() {
   137  					os.Setenv("HOME", prevHome)
   138  				})
   139  
   140  				When("$HOME is set", func() {
   141  					var (
   142  						tempDir string
   143  						err     error
   144  					)
   145  
   146  					BeforeEach(func() {
   147  						tempDir, err = ioutil.TempDir("", "")
   148  						Expect(err).ToNot(HaveOccurred())
   149  						os.Setenv("HOME", tempDir)
   150  
   151  						for _, filename := range []string{"abc", "def"} {
   152  							err = ioutil.WriteFile(filepath.Join(tempDir, filename), []byte{}, 0400)
   153  							Expect(err).ToNot(HaveOccurred())
   154  						}
   155  
   156  						for _, dir := range []string{"adir", "bdir"} {
   157  							err = os.Mkdir(filepath.Join(tempDir, dir), 0700)
   158  							Expect(err).ToNot(HaveOccurred())
   159  						}
   160  					})
   161  
   162  					AfterEach(func() {
   163  						err = os.RemoveAll(tempDir)
   164  						Expect(err).ToNot(HaveOccurred())
   165  					})
   166  
   167  					It("returns matching paths in $HOME", func() {
   168  						matches := subject.Complete(fmt.Sprintf("~%ca", os.PathSeparator))
   169  						Expect(matches).To(ConsistOf(
   170  							flags.Completion{Item: fmt.Sprintf("~%cabc", os.PathSeparator)},
   171  							flags.Completion{Item: fmt.Sprintf("~%cadir%c", os.PathSeparator, os.PathSeparator)},
   172  						))
   173  					})
   174  				})
   175  			})
   176  		})
   177  	}
   178  
   179  	BeforeEach(func() {
   180  		var err error
   181  		currentDir, err = os.Getwd()
   182  		Expect(err).ToNot(HaveOccurred())
   183  
   184  		tempDir, err = ioutil.TempDir("", "")
   185  		Expect(err).ToNot(HaveOccurred())
   186  
   187  		err = os.Chdir(tempDir)
   188  		Expect(err).ToNot(HaveOccurred())
   189  
   190  		for _, filename := range []string{"abc", "abd", "~abd", "tfg", "ABCD"} {
   191  			err = ioutil.WriteFile(filename, []byte{}, 0400)
   192  			Expect(err).ToNot(HaveOccurred())
   193  		}
   194  
   195  		for _, dir := range []string{"~add", "add", "aee"} {
   196  			err := os.Mkdir(dir, 0700)
   197  			Expect(err).ToNot(HaveOccurred())
   198  		}
   199  	})
   200  
   201  	AfterEach(func() {
   202  		err := os.Chdir(currentDir)
   203  		Expect(err).ToNot(HaveOccurred())
   204  		err = os.RemoveAll(tempDir)
   205  		Expect(err).ToNot(HaveOccurred())
   206  	})
   207  
   208  	Describe("Path", func() {
   209  		var path Path
   210  		testComplete(path)
   211  	})
   212  
   213  	Describe("PathWithExistenceCheck", func() {
   214  		var pathWithExistenceCheck PathWithExistenceCheck
   215  
   216  		BeforeEach(func() {
   217  			pathWithExistenceCheck = PathWithExistenceCheck("")
   218  		})
   219  
   220  		testComplete(pathWithExistenceCheck)
   221  
   222  		Describe("UnmarshalFlag", func() {
   223  			When("the path does not exist", func() {
   224  				It("returns a path does not exist error", func() {
   225  					err := pathWithExistenceCheck.UnmarshalFlag("./some-dir/some-file")
   226  					Expect(err).To(MatchError(&flags.Error{
   227  						Type:    flags.ErrRequired,
   228  						Message: "The specified path './some-dir/some-file' does not exist.",
   229  					}))
   230  				})
   231  			})
   232  
   233  			When("the path exists", func() {
   234  				When("the path is relative", func() {
   235  					It("expands the path", func() {
   236  						fullPath := filepath.Join(tempDir, "abc")
   237  						err := pathWithExistenceCheck.UnmarshalFlag("abc")
   238  						Expect(err).ToNot(HaveOccurred())
   239  						Expect(string(pathWithExistenceCheck)).To(matchers.MatchPath(fullPath))
   240  					})
   241  				})
   242  
   243  				When("the path is absolute", func() {
   244  					It("preserves the path", func() {
   245  						err := pathWithExistenceCheck.UnmarshalFlag(filepath.Join(tempDir, "abc"))
   246  						Expect(err).ToNot(HaveOccurred())
   247  						Expect(pathWithExistenceCheck).To(BeEquivalentTo(filepath.Join(tempDir, "abc")))
   248  					})
   249  				})
   250  			})
   251  		})
   252  	})
   253  
   254  	Describe("ManifestPathWithExistenceCheck", func() {
   255  		var manifestPathWithExistenceCheck ManifestPathWithExistenceCheck
   256  
   257  		BeforeEach(func() {
   258  			manifestPathWithExistenceCheck = ManifestPathWithExistenceCheck("")
   259  		})
   260  
   261  		testComplete(manifestPathWithExistenceCheck)
   262  
   263  		Describe("UnmarshalFlag", func() {
   264  			When("the path does not exist", func() {
   265  				It("returns a path does not exist error", func() {
   266  					err := manifestPathWithExistenceCheck.UnmarshalFlag("./some-dir/some-file")
   267  					Expect(err).To(MatchError(&flags.Error{
   268  						Type:    flags.ErrRequired,
   269  						Message: "The specified path './some-dir/some-file' does not exist.",
   270  					}))
   271  				})
   272  			})
   273  
   274  			When("the path is a directory, and exists, but does not contain a manifest.{yml,yaml} file", func() {
   275  				It("returns a path does not exist error", func() {
   276  					err := manifestPathWithExistenceCheck.UnmarshalFlag(tempDir)
   277  					Expect(err).To(MatchError(&flags.Error{
   278  						Type:    flags.ErrRequired,
   279  						Message: "The specified directory '" + tempDir + "' does not contain a file named 'manifest.yml'.",
   280  					}))
   281  				})
   282  			})
   283  
   284  			When("the path exists", func() {
   285  				It("sets the path", func() {
   286  					err := manifestPathWithExistenceCheck.UnmarshalFlag("abc")
   287  					Expect(err).ToNot(HaveOccurred())
   288  					Expect(manifestPathWithExistenceCheck).To(BeEquivalentTo("abc"))
   289  				})
   290  			})
   291  		})
   292  	})
   293  
   294  	Describe("JSONOrFileWithValidation", func() {
   295  		var jsonOrFile JSONOrFileWithValidation
   296  
   297  		BeforeEach(func() {
   298  			jsonOrFile = JSONOrFileWithValidation{}
   299  		})
   300  
   301  		testComplete(jsonOrFile)
   302  
   303  		Describe("UnmarshalFlag", func() {
   304  			When("the file exists", func() {
   305  				var tempPath string
   306  
   307  				When("the file has valid JSON", func() {
   308  					BeforeEach(func() {
   309  						tempPath = tempFile(`{"this is":"valid JSON"}`)
   310  					})
   311  
   312  					It("reads and unmarshals the JSON from the file", func() {
   313  						err := jsonOrFile.UnmarshalFlag(tempPath)
   314  						Expect(err).ToNot(HaveOccurred())
   315  						Expect(jsonOrFile).To(Equal(JSONOrFileWithValidation{
   316  							IsSet: true,
   317  							Value: map[string]interface{}{
   318  								"this is": "valid JSON",
   319  							},
   320  						}))
   321  					})
   322  				})
   323  
   324  				When("the file has invalid JSON", func() {
   325  					BeforeEach(func() {
   326  						tempPath = tempFile(`{"this is":"invalid JSON"`)
   327  					})
   328  
   329  					It("errors with the invalid configuration error", func() {
   330  						err := jsonOrFile.UnmarshalFlag(tempPath)
   331  						Expect(err).To(Equal(&flags.Error{
   332  							Type:    flags.ErrRequired,
   333  							Message: "Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object.",
   334  						}))
   335  					})
   336  				})
   337  			})
   338  
   339  			When("the JSON is invalid", func() {
   340  				It("errors with the invalid configuration error", func() {
   341  					err := jsonOrFile.UnmarshalFlag(`{"this is":"invalid JSON"`)
   342  					Expect(err).To(Equal(&flags.Error{
   343  						Type:    flags.ErrRequired,
   344  						Message: "Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object.",
   345  					}))
   346  				})
   347  			})
   348  
   349  			When("the JSON is valid", func() {
   350  				It("reads and unmarshals the JSON", func() {
   351  					err := jsonOrFile.UnmarshalFlag(`{"this is":"valid JSON"}`)
   352  					Expect(err).ToNot(HaveOccurred())
   353  					Expect(jsonOrFile).To(Equal(JSONOrFileWithValidation{
   354  						IsSet: true,
   355  						Value: map[string]interface{}{
   356  							"this is": "valid JSON",
   357  						},
   358  					}))
   359  				})
   360  			})
   361  		})
   362  	})
   363  
   364  	Describe("PathWithExistenceCheckOrURL", func() {
   365  		var pathWithExistenceCheckOrURL PathWithExistenceCheckOrURL
   366  
   367  		BeforeEach(func() {
   368  			pathWithExistenceCheckOrURL = PathWithExistenceCheckOrURL("")
   369  		})
   370  
   371  		testComplete(pathWithExistenceCheckOrURL)
   372  
   373  		Describe("UnmarshalFlag", func() {
   374  			When("the path is a URL", func() {
   375  				It("sets the path if it starts with 'http://'", func() {
   376  					err := pathWithExistenceCheckOrURL.UnmarshalFlag("http://example.com/payload.tgz")
   377  					Expect(err).ToNot(HaveOccurred())
   378  					Expect(pathWithExistenceCheckOrURL).To(BeEquivalentTo("http://example.com/payload.tgz"))
   379  				})
   380  
   381  				It("sets the path if it starts with 'https://'", func() {
   382  					err := pathWithExistenceCheckOrURL.UnmarshalFlag("https://example.com/payload.tgz")
   383  					Expect(err).ToNot(HaveOccurred())
   384  					Expect(pathWithExistenceCheckOrURL).To(BeEquivalentTo("https://example.com/payload.tgz"))
   385  				})
   386  			})
   387  
   388  			When("the path does not exist", func() {
   389  				It("returns a path does not exist error", func() {
   390  					err := pathWithExistenceCheckOrURL.UnmarshalFlag("./some-dir/some-file")
   391  					Expect(err).To(MatchError(&flags.Error{
   392  						Type:    flags.ErrRequired,
   393  						Message: "The specified path './some-dir/some-file' does not exist.",
   394  					}))
   395  				})
   396  			})
   397  
   398  			When("the path exists", func() {
   399  				It("sets the path", func() {
   400  					err := pathWithExistenceCheckOrURL.UnmarshalFlag("abc")
   401  					Expect(err).ToNot(HaveOccurred())
   402  					Expect(pathWithExistenceCheckOrURL).To(BeEquivalentTo("abc"))
   403  				})
   404  			})
   405  		})
   406  	})
   407  
   408  	Describe("PathWithAt", func() {
   409  		var pathWithAt PathWithAt
   410  
   411  		Describe("Complete", func() {
   412  			When("the prefix is empty", func() {
   413  				It("returns no matches", func() {
   414  					Expect(pathWithAt.Complete("")).To(BeEmpty())
   415  				})
   416  			})
   417  
   418  			When("the prefix doesn't start with @", func() {
   419  				It("returns no matches", func() {
   420  					Expect(pathWithAt.Complete("a@b")).To(BeEmpty())
   421  				})
   422  			})
   423  
   424  			When("the prefix starts with @", func() {
   425  				When("there are no characters after the @", func() {
   426  					It("returns all files and directories", func() {
   427  						matches := pathWithAt.Complete("@")
   428  						Expect(matches).To(ConsistOf(
   429  							flags.Completion{Item: "@abc"},
   430  							flags.Completion{Item: "@abd"},
   431  							flags.Completion{Item: fmt.Sprintf("@~add%c", os.PathSeparator)},
   432  							flags.Completion{Item: "@~abd"},
   433  							flags.Completion{Item: fmt.Sprintf("@add%c", os.PathSeparator)},
   434  							flags.Completion{Item: fmt.Sprintf("@aee%c", os.PathSeparator)},
   435  							flags.Completion{Item: "@tfg"},
   436  							flags.Completion{Item: "@ABCD"},
   437  						))
   438  					})
   439  				})
   440  
   441  				When("there are characters after the @", func() {
   442  					When("there are matching paths", func() {
   443  						It("returns the matching paths", func() {
   444  							matches := pathWithAt.Complete("@a")
   445  							Expect(matches).To(ConsistOf(
   446  								flags.Completion{Item: "@abc"},
   447  								flags.Completion{Item: "@abd"},
   448  								flags.Completion{Item: fmt.Sprintf("@add%c", os.PathSeparator)},
   449  								flags.Completion{Item: fmt.Sprintf("@aee%c", os.PathSeparator)},
   450  							))
   451  						})
   452  
   453  						It("is case sensitive", func() {
   454  							matches := pathWithAt.Complete("@A")
   455  							Expect(matches).To(ConsistOf(
   456  								flags.Completion{Item: "@ABCD"},
   457  							))
   458  						})
   459  					})
   460  
   461  					When("there are no matching paths", func() {
   462  						It("returns no matches", func() {
   463  							Expect(pathWithAt.Complete("@z")).To(BeEmpty())
   464  						})
   465  					})
   466  				})
   467  			})
   468  
   469  			When("the prefix is @~/", func() {
   470  				var prevHome string
   471  
   472  				BeforeEach(func() {
   473  					prevHome = os.Getenv("HOME")
   474  				})
   475  
   476  				AfterEach(func() {
   477  					os.Setenv("HOME", prevHome)
   478  				})
   479  
   480  				When("$HOME is set", func() {
   481  					var (
   482  						tempDir string
   483  						err     error
   484  					)
   485  
   486  					BeforeEach(func() {
   487  						tempDir, err = ioutil.TempDir("", "")
   488  						Expect(err).ToNot(HaveOccurred())
   489  						os.Setenv("HOME", tempDir)
   490  
   491  						for _, filename := range []string{"abc", "def"} {
   492  							err = ioutil.WriteFile(filepath.Join(tempDir, filename), []byte{}, 0400)
   493  							Expect(err).ToNot(HaveOccurred())
   494  						}
   495  
   496  						for _, dir := range []string{"adir", "bdir"} {
   497  							err = os.Mkdir(filepath.Join(tempDir, dir), 0700)
   498  							Expect(err).ToNot(HaveOccurred())
   499  						}
   500  					})
   501  
   502  					AfterEach(func() {
   503  						err = os.RemoveAll(tempDir)
   504  						Expect(err).ToNot(HaveOccurred())
   505  					})
   506  
   507  					It("returns matching paths in $HOME", func() {
   508  						matches := pathWithAt.Complete(fmt.Sprintf("@~%c", os.PathSeparator))
   509  						Expect(matches).To(ConsistOf(
   510  							flags.Completion{Item: fmt.Sprintf("@~%cabc", os.PathSeparator)},
   511  							flags.Completion{Item: fmt.Sprintf("@~%cdef", os.PathSeparator)},
   512  							flags.Completion{Item: fmt.Sprintf("@~%cadir%c", os.PathSeparator, os.PathSeparator)},
   513  							flags.Completion{Item: fmt.Sprintf("@~%cbdir%c", os.PathSeparator, os.PathSeparator)},
   514  						))
   515  					})
   516  				})
   517  			})
   518  
   519  			When("the prefix starts with @~/", func() {
   520  				var prevHome string
   521  
   522  				BeforeEach(func() {
   523  					prevHome = os.Getenv("HOME")
   524  				})
   525  
   526  				AfterEach(func() {
   527  					os.Setenv("HOME", prevHome)
   528  				})
   529  
   530  				When("$HOME is set", func() {
   531  					var (
   532  						tempDir string
   533  						err     error
   534  					)
   535  
   536  					BeforeEach(func() {
   537  						tempDir, err = ioutil.TempDir("", "")
   538  						Expect(err).ToNot(HaveOccurred())
   539  						os.Setenv("HOME", tempDir)
   540  
   541  						for _, filename := range []string{"abc", "def"} {
   542  							err = ioutil.WriteFile(filepath.Join(tempDir, filename), []byte{}, 0400)
   543  							Expect(err).ToNot(HaveOccurred())
   544  						}
   545  
   546  						for _, dir := range []string{"adir", "bdir"} {
   547  							err = os.Mkdir(filepath.Join(tempDir, dir), 0700)
   548  							Expect(err).ToNot(HaveOccurred())
   549  						}
   550  					})
   551  
   552  					AfterEach(func() {
   553  						err = os.RemoveAll(tempDir)
   554  						Expect(err).ToNot(HaveOccurred())
   555  					})
   556  
   557  					It("returns matching paths in $HOME", func() {
   558  						matches := pathWithAt.Complete(fmt.Sprintf("@~%ca", os.PathSeparator))
   559  						Expect(matches).To(ConsistOf(
   560  							flags.Completion{Item: fmt.Sprintf("@~%cabc", os.PathSeparator)},
   561  							flags.Completion{Item: fmt.Sprintf("@~%cadir%c", os.PathSeparator, os.PathSeparator)},
   562  						))
   563  					})
   564  				})
   565  			})
   566  		})
   567  	})
   568  
   569  	Describe("PathWithBool", func() {
   570  		var pathWithBool PathWithBool
   571  
   572  		Describe("Complete", func() {
   573  			When("the prefix is empty", func() {
   574  				It("returns bool choices and all files and directories", func() {
   575  					matches := pathWithBool.Complete("")
   576  					Expect(matches).To(ConsistOf(
   577  						flags.Completion{Item: "true"},
   578  						flags.Completion{Item: "false"},
   579  						flags.Completion{Item: "abc"},
   580  						flags.Completion{Item: "abd"},
   581  						flags.Completion{Item: fmt.Sprintf("add%c", os.PathSeparator)},
   582  						flags.Completion{Item: "~abd"},
   583  						flags.Completion{Item: fmt.Sprintf("~add%c", os.PathSeparator)},
   584  						flags.Completion{Item: fmt.Sprintf("aee%c", os.PathSeparator)},
   585  						flags.Completion{Item: "tfg"},
   586  						flags.Completion{Item: "ABCD"},
   587  					))
   588  				})
   589  			})
   590  
   591  			When("the prefix is not empty", func() {
   592  				When("there are matching bool/paths", func() {
   593  					It("returns the matching bool/paths", func() {
   594  						matches := pathWithBool.Complete("t")
   595  						Expect(matches).To(ConsistOf(
   596  							flags.Completion{Item: "true"},
   597  							flags.Completion{Item: "tfg"},
   598  						))
   599  					})
   600  
   601  					It("paths are case sensitive", func() {
   602  						matches := pathWithBool.Complete("A")
   603  						Expect(matches).To(ConsistOf(
   604  							flags.Completion{Item: "ABCD"},
   605  						))
   606  					})
   607  
   608  					It("bools are not case sensitive", func() {
   609  						matches := pathWithBool.Complete("Tr")
   610  						Expect(matches).To(ConsistOf(
   611  							flags.Completion{Item: "true"},
   612  						))
   613  					})
   614  				})
   615  
   616  				When("there are no matching bool/paths", func() {
   617  					It("returns no matches", func() {
   618  						Expect(pathWithBool.Complete("z")).To(BeEmpty())
   619  					})
   620  				})
   621  			})
   622  		})
   623  	})
   624  })