Repository: exercism/cli
Pull request #454 (2017-08-18 06:04:17)

[WIP] [nextercism] Transmute submit arguments into file paths
This is a little bit of dirty alchemy, and I truly do not know if it's the right approach.

It builds on top of #453, so if you've seen that, you only have to look at the second commit in this PR.

When someone submits, they might specify a directory. Or files. Or just the name of an exercise. Depending on what the arguments are, there are numerous ways in which everything can go wrong.

So we need to figure out a few things:

- what is the root solution directory that they're trying to submit
- are they trying to specify specific files within that directory

If they're specifying specific files, then we don't want to filter out the README, test suite, and metadata automatically. If they're saying it should be submitted, then it should be submitted.

But if they're specifying a directory, then we are going to need to walk that directory, find the files, and filter out stuff we don't want to automatically push up to the site.

This function munges the arguments that are passed to the submit command, to figure out how it's going to have to handle things.

kytrinyx: > Kinda feels like an exercism exercise

Haha yes, it does. It has that smallish scope. :-)

I've pushed up the typo fix, and will merge this when the build goes green.

Patch:
diff --git a/fixtures/transmission/creatures/hummingbird/.solution.json b/fixtures/transmission/creatures/hummingbird/.solution.json
new file mode 100644
index 0000000..e69de29
diff --git a/fixtures/transmission/creatures/hummingbird/feeder/sugar.txt b/fixtures/transmission/creatures/hummingbird/feeder/sugar.txt
new file mode 100644
index 0000000..e69de29
diff --git a/fixtures/transmission/creatures/hummingbird/hummingbird.txt b/fixtures/transmission/creatures/hummingbird/hummingbird.txt
new file mode 100644
index 0000000..e69de29
diff --git a/workspace/transmission.go b/workspace/transmission.go
new file mode 100644
index 0000000..f277779
--- /dev/null
+++ b/workspace/transmission.go
@@ -0,0 +1,59 @@
+package workspace
+
+import (
+	"errors"
+	"path/filepath"
+)
+
+// Transmission is the data necessary to submit a solution.
+type Transmission struct {
+	Files   []string
+	Dir     string
+	argDirs []string
+}
+
+// NewTransmission processes the arguments to the submit command to prep a submission.
+func NewTransmission(root string, args []string) (*Transmission, error) {
+	tx := &Transmission{}
+	for _, arg := range args {
+		pt, err := DetectPathType(arg)
+		if err != nil {
+			return nil, err
+		}
+		if pt == TypeFile {
+			arg, err = filepath.Abs(arg)
+			if err != nil {
+				return nil, err
+			}
+			tx.Files = append(tx.Files, arg)
+			continue
+		}
+		// For our purposes, if it's not a file then it's a directory.
+		tx.argDirs = append(tx.argDirs, arg)
+	}
+	if len(tx.argDirs) > 1 {
+		return nil, errors.New("more than one dir")
+	}
+	if len(tx.argDirs) > 0 && len(tx.Files) > 0 {
+		return nil, errors.New("mixing files and dirs")
+	}
+	if len(tx.Files) > 0 {
+		ws := New(root)
+		parents := map[string]bool{}
+		for _, file := range tx.Files {
+			dir, err := ws.SolutionDir(file)
+			if err != nil {
+				return nil, err
+			}
+			parents[dir] = true
+			tx.Dir = dir
+		}
+		if len(parents) > 1 {
+			return nil, errors.New("files are from more than one solution")
+		}
+	}
+	if len(tx.argDirs) == 1 {
+		tx.Dir = tx.argDirs[0]
+	}
+	return tx, nil
+}


Test patch:
diff --git a/workspace/transmission_test.go b/workspace/transmission_test.go
new file mode 100644
index 0000000..828d40a
--- /dev/null
+++ b/workspace/transmission_test.go
@@ -0,0 +1,96 @@
+package workspace
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"runtime"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewTransmission(t *testing.T) {
+	_, cwd, _, _ := runtime.Caller(0)
+	root := filepath.Join(cwd, "..", "..", "fixtures", "transmission")
+	dirBird := filepath.Join(root, "creatures", "hummingbird")
+	dirFeeder := filepath.Join(dirBird, "feeder")
+	fileBird := filepath.Join(dirBird, "hummingbird.txt")
+	fileSugar := filepath.Join(dirFeeder, "sugar.txt")
+
+	tests := []struct {
+		desc string
+		args []string
+		ok   bool
+		tx   *Transmission
+	}{
+		{
+			desc: "more than one dir",
+			args: []string{dirBird, dirFeeder},
+			ok:   false,
+		},
+		{
+			desc: "a file and a dir",
+			args: []string{dirBird, fileBird},
+			ok:   false,
+		},
+		{
+			desc: "just one file",
+			args: []string{fileBird},
+			ok:   true,
+			tx:   &Transmission{Files: []string{fileBird}, Dir: dirBird},
+		},
+		{
+			desc: "multiple files",
+			args: []string{fileBird, fileSugar},
+			ok:   true,
+			tx:   &Transmission{Files: []string{fileBird, fileSugar}, Dir: dirBird},
+		},
+		{
+			desc: "one dir",
+			args: []string{dirBird},
+			ok:   true,
+			tx:   &Transmission{Files: nil, Dir: dirBird},
+		},
+		{
+			desc: "multiple exercise names",
+			args: []string{"hummingbird", "bear"},
+			ok:   false,
+		},
+		{
+			desc: "one exercise name",
+			args: []string{"hummingbird"},
+			ok:   true,
+			tx:   &Transmission{Files: nil, Dir: "hummingbird"},
+		},
+	}
+
+	for _, test := range tests {
+		tx, err := NewTransmission(root, test.args)
+		if test.ok {
+			assert.NoError(t, err, test.desc)
+		} else {
+			assert.Error(t, err, test.desc)
+		}
+
+		if test.tx != nil {
+			assert.Equal(t, test.tx.Files, tx.Files, test.desc)
+			assert.Equal(t, test.tx.Dir, tx.Dir, test.desc)
+		}
+	}
+}
+
+func TestTransmissionWithRelativePath(t *testing.T) {
+	// This is really dirty, but I need to make sure that we turn relative paths into absolute paths.
+	err := ioutil.WriteFile(".solution.json", []byte("{}"), os.FileMode(0755))
+	assert.NoError(t, err)
+	defer os.Remove(".solution.json")
+
+	_, cwd, _, _ := runtime.Caller(0)
+	dir := filepath.Dir(filepath.Dir(cwd))
+	file := filepath.Base(cwd)
+	tx, err := NewTransmission(dir, []string{file})
+	if assert.NoError(t, err) {
+		assert.Equal(t, cwd, tx.Files[0])
+	}
+}