From 23077b75410d6fab51762236f195d92bd559fc6a Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Fri, 7 Jul 2017 12:14:06 +0200 Subject: many more `rtree get` tests --- pkg/rtree/get_test.go | 136 +++++++++++++++++++++++++++++++++++++++++++---- pkg/rtree/index.go | 3 +- pkg/rtree/shared_test.go | 96 ++++++++++++++++++++++++++------- 3 files changed, 205 insertions(+), 30 deletions(-) (limited to 'pkg/rtree') diff --git a/pkg/rtree/get_test.go b/pkg/rtree/get_test.go index be7e092..8ab21f1 100644 --- a/pkg/rtree/get_test.go +++ b/pkg/rtree/get_test.go @@ -19,24 +19,138 @@ package rtree import ( + "fmt" "path/filepath" "testing" + + "github.com/majewsky/gofu/pkg/cli" ) -func TestGetRepoFromIndex(t *testing.T) { - idx := Index{ - Repos: []*Repo{ - { - CheckoutPath: "github.com/git/git", - Remotes: []Remote{ - {Name: "origin", URL: "gh:git/git"}, - }, +var testIndexWithTwoRepos = Index{ + Repos: []*Repo{ + { + CheckoutPath: "github.com/foo/bar", + Remotes: []Remote{ + {Name: "origin", URL: "https://github.com/foo/bar"}, + }, + }, + { + CheckoutPath: "github.com/git/git", + Remotes: []Remote{ + {Name: "origin", URL: "gh:git/git"}, }, }, - } + }, +} + +func TestGetExistingRepo(t *testing.T) { Test{ Args: []string{"get", "gh:git/git"}, - Index: idx, + Index: testIndexWithTwoRepos, + ExpectOutput: filepath.Join(RootPath, "/github.com/git/git") + "\n", + }.Run(t) +} + +func TestGetExistingRepoWithoutShortcut(t *testing.T) { + Test{ + Args: []string{"get", "https://github.com/git/git"}, + Index: testIndexWithTwoRepos, ExpectOutput: filepath.Join(RootPath, "/github.com/git/git") + "\n", - }.Run(t, "TestGetRepoFromIndex") + }.Run(t) +} + +func TestGetNewRepo(t *testing.T) { + target := filepath.Join(RootPath, "/github.com/another/repo") + + Test{ + Args: []string{"get", "gh:another/repo"}, + Index: testIndexWithTwoRepos, + ExpectOutput: target + "\n", + ExpectExecution: []RecordedCommand{ + {Cmd: cli.Command{ + Program: []string{"git", "clone", "gh:another/repo", target}, + }}, + }, + ExpectIndex: &Index{ + Repos: []*Repo{ + { + CheckoutPath: "github.com/another/repo", + Remotes: []Remote{ + {Name: "origin", URL: "gh:another/repo"}, + }, + }, + testIndexWithTwoRepos.Repos[0], + testIndexWithTwoRepos.Repos[1], + }, + }, + }.Run(t) +} + +func TestGetNewForkAsRemote(t *testing.T) { + target := filepath.Join(RootPath, "/github.com/git/git") + Test{ + Args: []string{"get", "https://example.com/git"}, + Index: testIndexWithTwoRepos, + Input: fmt.Sprintf("add as remote to %s\nmyfork\n", target), + ExpectOutput: target + "\n", + ExpectError: fmt.Sprintf( + "Found possible fork candidates. What to do? -> add as remote to %s\n"+ + "Existing remotes:\n\t(origin) gh:git/git\n"+ + "Enter remote name for https://example.com/git: myfork\n", + target, + ), + ExpectExecution: []RecordedCommand{ + {Cmd: cli.Command{ + Program: []string{"git", "remote", "add", "myfork", "https://example.com/git"}, + WorkDir: target, + }}, + {Cmd: cli.Command{ + Program: []string{"git", "remote", "update", "myfork"}, + WorkDir: target, + }}, + }, + ExpectIndex: &Index{ + Repos: []*Repo{ + testIndexWithTwoRepos.Repos[0], + { + CheckoutPath: "github.com/git/git", + Remotes: []Remote{ + {Name: "origin", URL: "gh:git/git"}, + {Name: "myfork", URL: "https://example.com/git"}, + }, + }, + }, + }, + }.Run(t) +} + +func TestGetNewForkAsSeparate(t *testing.T) { + target := filepath.Join(RootPath, "/example.com/git") + Test{ + Args: []string{"get", "https://example.com/git"}, + Index: testIndexWithTwoRepos, + Input: fmt.Sprintf("clone to %s\n", target), + ExpectOutput: target + "\n", + ExpectError: fmt.Sprintf( + "Found possible fork candidates. What to do? -> clone to %s\n", + target, + ), + ExpectExecution: []RecordedCommand{ + {Cmd: cli.Command{ + Program: []string{"git", "clone", "https://example.com/git", target}, + }}, + }, + ExpectIndex: &Index{ + Repos: []*Repo{ + { + CheckoutPath: "example.com/git", + Remotes: []Remote{ + {Name: "origin", URL: "https://example.com/git"}, + }, + }, + testIndexWithTwoRepos.Repos[0], + testIndexWithTwoRepos.Repos[1], + }, + }, + }.Run(t) } diff --git a/pkg/rtree/index.go b/pkg/rtree/index.go index c7d6097..88e8b2d 100644 --- a/pkg/rtree/index.go +++ b/pkg/rtree/index.go @@ -92,6 +92,7 @@ func (r reposByAbsPath) Swap(i, j int) { r[i], r[j] = r[j], r[i] } //Write writes the index file to disk. func (i *Index) Write() error { + sort.Sort(reposByAbsPath(i.Repos)) buf, err := yaml.Marshal(i) if err != nil { return err @@ -304,7 +305,7 @@ func (i *Index) FindRepo(remoteURL string, allowClone bool) (*Repo, error) { //find the repo selected by the user var target *Repo for _, repo := range candidates { - if target.CheckoutPath == selection { + if repo.CheckoutPath == selection { target = repo break } diff --git a/pkg/rtree/shared_test.go b/pkg/rtree/shared_test.go index 209acb4..cad9378 100644 --- a/pkg/rtree/shared_test.go +++ b/pkg/rtree/shared_test.go @@ -20,11 +20,14 @@ package rtree import ( "bytes" + "errors" "fmt" + "io" "io/ioutil" "os" "os/exec" "path/filepath" + "strings" "testing" "github.com/majewsky/gofu/pkg/cli" @@ -56,47 +59,49 @@ func TestMain(m *testing.M) { //Test describes a call to Main(), the environment that's given to it, and the //assertions that are checked after the call returns. type Test struct { - Args []string - Input string - Index Index - ExpectFailure bool - ExpectOutput string - ExpectError string - ExpectIndex *Index //if nil, .Index will be used instead + Args []string + Input string + Index Index + ExpectFailure bool + ExpectOutput string + ExpectError string + ExpectIndex *Index //if nil, .Index will be used instead + ExpectExecution []RecordedCommand } -func (test Test) Run(t *testing.T, testName string) { +func (test Test) Run(t *testing.T) { //write index file, if any - IndexPath = filepath.Join(indexTmpDir, testName+".yaml") + IndexPath = filepath.Join(indexTmpDir, t.Name()+".yaml") if test.Index.Repos != nil { err := test.Index.Write() if err != nil { - t.Fatalf("%s: cannot write index to %s: %s", testName, IndexPath, err.Error()) + t.Fatalf("%s: cannot write index to %s: %s", t.Name(), IndexPath, err.Error()) } } //setup cli.Interface for test var stdout bytes.Buffer var stderr bytes.Buffer - cli.SetupInterface(bytes.NewReader([]byte(test.Input)), &stdout, &stderr, nil) + cs := CommandSimulator{Cmd: test.ExpectExecution} + cli.SetupInterface(bytes.NewReader([]byte(test.Input)), &stdout, &stderr, cs.Next) //check exit code exitCode := Exec(test.Args) switch { case exitCode == 0 && test.ExpectFailure: - t.Errorf("%s: expected failure, but returned success", testName) + t.Errorf("%s: expected failure, but returned success", t.Name()) case exitCode != 0 && !test.ExpectFailure: - t.Errorf("%s: expected success, but returned failure", testName) + t.Errorf("%s: expected success, but returned failure", t.Name()) } //check output output := string(stdout.Bytes()) if output != test.ExpectOutput { - t.Errorf("%s: expected stdout %#v, but got %#v", testName, test.ExpectOutput, output) + t.Errorf("%s: expected stdout %#v, but got %#v", t.Name(), test.ExpectOutput, output) } output = string(stderr.Bytes()) if output != test.ExpectError { - t.Errorf("%s: expected stderr %#v, but got %#v", testName, test.ExpectError, output) + t.Errorf("%s: expected stderr %#v, but got %#v", t.Name(), test.ExpectError, output) } //check index @@ -110,17 +115,72 @@ func (test Test) Run(t *testing.T, testName string) { } actualIdxStr, err := ioutil.ReadFile(IndexPath) if err != nil { - t.Fatalf("%s: could not read index from %s: %s", testName, IndexPath, err.Error()) + t.Fatalf("%s: could not read index from %s: %s", t.Name(), IndexPath, err.Error()) } if string(expectedIdxStr) != string(actualIdxStr) { - t.Errorf("%s: index does not match expectation after test; diff follows", testName) + t.Errorf("%s: index does not match expectation after test; diff follows", t.Name()) cmd := exec.Command("diff", "-u", "-", IndexPath) cmd.Stdin = bytes.NewReader([]byte(expectedIdxStr)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - err := cmd.Wait() + err := cmd.Run() if err != nil { t.Fatal(err.Error()) } } } + +type RecordedCommand struct { + Cmd cli.Command + Stdout string + Stderr string + Fails bool +} + +//CommandSimulator implements the cli.CommandRunner interface (via its Next +//method). When a cli.Command is given to Next(), it is matched with the next +//command in the .Cmd list, and the result from that RecordedCommand is +//returned. If the given Command is different from the one expected (or if the +//.Cmd list has been exhausted), an error is returned. +type CommandSimulator struct { + Cmd []RecordedCommand + idx int +} + +func (s *CommandSimulator) Next(c cli.Command, stdin io.Reader, stdout, stderr io.Writer) error { + //take next RecordedCommand from list + if s.idx >= len(s.Cmd) { + return errors.New("got command to execute, but recorded commands have been exhausted") + } + sc := s.Cmd[s.idx] + s.idx++ + + //check if the given Command matches the expectation + if !areStringListsEqual(sc.Cmd.Program, c.Program) { + return fmt.Errorf("expected command %#v, but got %#v", + strings.Join(sc.Cmd.Program, " "), strings.Join(c.Program, " "), + ) + } + if sc.Cmd.WorkDir != c.WorkDir { + return fmt.Errorf("expected command workdir %s, but got %s", sc.Cmd.WorkDir, c.WorkDir) + } + + stdout.Write([]byte(sc.Stdout)) + stderr.Write([]byte(sc.Stderr)) + if sc.Fails { + return fmt.Errorf("command %#v has failed", strings.Join(c.Program, " ")) + } + return nil +} + +func areStringListsEqual(a []string, b []string) bool { + if len(a) != len(b) { + return false + } + for idx := range a { + if a[idx] != b[idx] { + return false + } + } + return true +} -- cgit v1.3.1