aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/cli/query.go2
-rw-r--r--pkg/rtree/get_test.go136
-rw-r--r--pkg/rtree/index.go3
-rw-r--r--pkg/rtree/shared_test.go96
4 files changed, 207 insertions, 30 deletions
diff --git a/pkg/cli/query.go b/pkg/cli/query.go
index aa8fa50..65cb2a5 100644
--- a/pkg/cli/query.go
+++ b/pkg/cli/query.go
@@ -273,6 +273,8 @@ func (t *pipeTUI) Query(prompt string, choices ...Choice) (string, error) {
if err != nil {
return str, err
}
+ str = strings.TrimSpace(str)
+
//prefer exact match on choice.Text
for _, choice := range choices {
if choice.Text == str {
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
+}