Skip to content

Commit

Permalink
Merge pull request exercism#723 from jdsutherland/metadata-hidden-subdir
Browse files Browse the repository at this point in the history
Metadata hidden subdir
  • Loading branch information
Katrina Owen authored Sep 4, 2018
2 parents b7a2a41 + dc2feb5 commit 5185642
Show file tree
Hide file tree
Showing 19 changed files with 277 additions and 76 deletions.
4 changes: 2 additions & 2 deletions cmd/download_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ func TestDownload(t *testing.T) {
targetDir := filepath.Join(tmpDir, tc.expectedDir)
assertDownloadedCorrectFiles(t, targetDir)

path := filepath.Join(targetDir, "bogus-track", "bogus-exercise", ".solution.json")
b, err := ioutil.ReadFile(path)
dir := filepath.Join(targetDir, "bogus-track", "bogus-exercise")
b, err := ioutil.ReadFile(workspace.NewExerciseFromDir(dir).MetadataFilepath())
var s workspace.Solution
err = json.Unmarshal(b, &s)
assert.NoError(t, err)
Expand Down
8 changes: 7 additions & 1 deletion cmd/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,13 @@ func runSubmit(cfg config.Config, flags *pflag.FlagSet, args []string) error {
}

exercise := workspace.NewExerciseFromDir(exerciseDir)

migrationStatus, err := exercise.MigrateLegacyMetadataFile()
if err != nil {
return err
}
if verbose, _ := flags.GetBool("verbose"); verbose {
fmt.Fprintf(os.Stderr, migrationStatus.String())
}
solution, err := workspace.NewSolution(exerciseDir)
if err != nil {
return err
Expand Down
64 changes: 64 additions & 0 deletions cmd/submit_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -185,6 +186,69 @@ func TestSubmitFiles(t *testing.T) {
assert.Equal(t, "This is the readme.", submittedFiles["README.md"])
}

func TestLegacySolutionMetadataMigration(t *testing.T) {
oldOut := Out
oldErr := Err
Out = ioutil.Discard
Err = ioutil.Discard
defer func() {
Out = oldOut
Err = oldErr
}()
// The fake endpoint will populate this when it receives the call from the command.
submittedFiles := map[string]string{}
ts := fakeSubmitServer(t, submittedFiles)
defer ts.Close()

tmpDir, err := ioutil.TempDir("", "legacy-metadata-file")
defer os.RemoveAll(tmpDir)
assert.NoError(t, err)

dir := filepath.Join(tmpDir, "bogus-track", "bogus-exercise")
os.MkdirAll(dir, os.FileMode(0755))

solution := &workspace.Solution{
ID: "bogus-solution-uuid",
Track: "bogus-track",
Exercise: "bogus-exercise",
URL: "http://example.com/bogus-url",
IsRequester: true,
}
b, err := json.Marshal(solution)
assert.NoError(t, err)
exercise := workspace.NewExerciseFromDir(dir)
err = ioutil.WriteFile(exercise.LegacyMetadataFilepath(), b, os.FileMode(0600))
assert.NoError(t, err)

file := filepath.Join(dir, "file.txt")
err = ioutil.WriteFile(file, []byte("This is a file."), os.FileMode(0755))
assert.NoError(t, err)

v := viper.New()
v.Set("token", "abc123")
v.Set("workspace", tmpDir)
v.Set("apibaseurl", ts.URL)
cfg := config.Config{
Persister: config.InMemoryPersister{},
Dir: tmpDir,
UserViperConfig: v,
}

ok, _ := exercise.HasLegacyMetadata()
assert.True(t, ok)
ok, _ = exercise.HasMetadata()
assert.False(t, ok)

err = runSubmit(cfg, pflag.NewFlagSet("fake", pflag.PanicOnError), []string{file})
assert.NoError(t, err)
assert.Equal(t, "This is a file.", submittedFiles["file.txt"])

ok, _ = exercise.HasLegacyMetadata()
assert.False(t, ok)
ok, _ = exercise.HasMetadata()
assert.True(t, ok)
}

func TestSubmitWithEmptyFile(t *testing.T) {
oldOut := Out
oldErr := Err
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
13 changes: 0 additions & 13 deletions visibility/hide_file.go

This file was deleted.

41 changes: 0 additions & 41 deletions visibility/hide_file_windows.go

This file was deleted.

63 changes: 62 additions & 1 deletion workspace/exercise.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ func (e Exercise) Filepath() string {

// MetadataFilepath is the absolute path to the exercise metadata.
func (e Exercise) MetadataFilepath() string {
return filepath.Join(e.Filepath(), solutionFilename)
return filepath.Join(e.Filepath(), metadataFilepath)
}

// LegacyMetadataFilepath is the absolute path to the legacy exercise metadata.
func (e Exercise) LegacyMetadataFilepath() string {
return filepath.Join(e.Filepath(), legacySolutionFilename)
}

// MetadataDir returns the directory that the exercise metadata lives in.
Expand All @@ -59,3 +64,59 @@ func (e Exercise) HasMetadata() (bool, error) {
}
return false, err
}

// HasLegacyMetadata checks for the presence of a legacy exercise metadata file.
// If there is no such file, it could also be an unrelated directory.
func (e Exercise) HasLegacyMetadata() (bool, error) {
_, err := os.Lstat(e.LegacyMetadataFilepath())
if os.IsNotExist(err) {
return false, nil
}
if err == nil {
return true, nil
}
return false, err
}

// MigrationStatus represents the result of migrating a legacy metadata file.
type MigrationStatus int

// MigrationStatus
const (
MigrationStatusNoop MigrationStatus = iota
MigrationStatusMigrated
MigrationStatusRemoved
)

func (m MigrationStatus) String() string {
switch m {
case MigrationStatusMigrated:
return "\nMigrated metadata\n"
case MigrationStatusRemoved:
return "\nRemoved legacy metadata\n"
default:
return ""
}
}

// MigrateLegacyMetadataFile migrates a legacy metadata file to the modern location.
// This is a noop if the metadata file isn't legacy.
// If both legacy and modern metadata files exist, the legacy file will be deleted.
func (e Exercise) MigrateLegacyMetadataFile() (MigrationStatus, error) {
if ok, _ := e.HasLegacyMetadata(); !ok {
return MigrationStatusNoop, nil
}
if err := os.MkdirAll(filepath.Dir(e.MetadataFilepath()), os.FileMode(0755)); err != nil {
return MigrationStatusNoop, err
}
if ok, _ := e.HasMetadata(); !ok {
if err := os.Rename(e.LegacyMetadataFilepath(), e.MetadataFilepath()); err != nil {
return MigrationStatusNoop, err
}
return MigrationStatusMigrated, nil
}
if err := os.Remove(e.LegacyMetadataFilepath()); err != nil {
return MigrationStatusNoop, err
}
return MigrationStatusRemoved, nil
}
125 changes: 123 additions & 2 deletions workspace/exercise_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ func TestHasMetadata(t *testing.T) {
exerciseA := Exercise{Root: ws, Track: "bogus-track", Slug: "apple"}
exerciseB := Exercise{Root: ws, Track: "bogus-track", Slug: "banana"}

err = os.MkdirAll(filepath.Join(exerciseA.Filepath()), os.FileMode(0755))
err = os.MkdirAll(filepath.Dir(exerciseA.MetadataFilepath()), os.FileMode(0755))
assert.NoError(t, err)
err = os.MkdirAll(filepath.Join(exerciseB.Filepath()), os.FileMode(0755))
err = os.MkdirAll(filepath.Dir(exerciseB.MetadataFilepath()), os.FileMode(0755))
assert.NoError(t, err)

err = ioutil.WriteFile(exerciseA.MetadataFilepath(), []byte{}, os.FileMode(0600))
Expand All @@ -34,6 +34,31 @@ func TestHasMetadata(t *testing.T) {
assert.False(t, ok)
}

func TestHasLegacyMetadata(t *testing.T) {
ws, err := ioutil.TempDir("", "fake-workspace")
defer os.RemoveAll(ws)
assert.NoError(t, err)

exerciseA := Exercise{Root: ws, Track: "bogus-track", Slug: "apple"}
exerciseB := Exercise{Root: ws, Track: "bogus-track", Slug: "banana"}

err = os.MkdirAll(filepath.Dir(exerciseA.LegacyMetadataFilepath()), os.FileMode(0755))
assert.NoError(t, err)
err = os.MkdirAll(filepath.Dir(exerciseB.LegacyMetadataFilepath()), os.FileMode(0755))
assert.NoError(t, err)

err = ioutil.WriteFile(exerciseA.LegacyMetadataFilepath(), []byte{}, os.FileMode(0600))
assert.NoError(t, err)

ok, err := exerciseA.HasLegacyMetadata()
assert.NoError(t, err)
assert.True(t, ok)

ok, err = exerciseB.HasLegacyMetadata()
assert.NoError(t, err)
assert.False(t, ok)
}

func TestNewFromDir(t *testing.T) {
dir := filepath.Join("something", "another", "whatever", "the-track", "the-exercise")

Expand All @@ -42,3 +67,99 @@ func TestNewFromDir(t *testing.T) {
assert.Equal(t, "the-track", exercise.Track)
assert.Equal(t, "the-exercise", exercise.Slug)
}

func TestMigrationStatusString(t *testing.T) {
assert.Equal(t, "\nMigrated metadata\n", MigrationStatusMigrated.String())
assert.Equal(t, "\nRemoved legacy metadata\n", MigrationStatusRemoved.String())
assert.Equal(t, "", MigrationStatusNoop.String())
assert.Equal(t, "", MigrationStatus(-1).String())
}

func TestMigrateLegacyMetadataFileWithoutLegacy(t *testing.T) {
ws, err := ioutil.TempDir("", "fake-workspace")
defer os.RemoveAll(ws)
assert.NoError(t, err)

exercise := Exercise{Root: ws, Track: "bogus-track", Slug: "no-legacy"}
metadataFilepath := exercise.MetadataFilepath()
err = os.MkdirAll(filepath.Dir(metadataFilepath), os.FileMode(0755))
assert.NoError(t, err)

err = ioutil.WriteFile(metadataFilepath, []byte{}, os.FileMode(0600))
assert.NoError(t, err)

ok, _ := exercise.HasLegacyMetadata()
assert.False(t, ok)
ok, _ = exercise.HasMetadata()
assert.True(t, ok)

status, err := exercise.MigrateLegacyMetadataFile()
assert.Equal(t, MigrationStatusNoop, status)
assert.NoError(t, err)

ok, _ = exercise.HasLegacyMetadata()
assert.False(t, ok)
ok, _ = exercise.HasMetadata()
assert.True(t, ok)
}

func TestMigrateLegacyMetadataFileWithLegacy(t *testing.T) {
ws, err := ioutil.TempDir("", "fake-workspace")
defer os.RemoveAll(ws)
assert.NoError(t, err)

exercise := Exercise{Root: ws, Track: "bogus-track", Slug: "legacy"}
legacyMetadataFilepath := exercise.LegacyMetadataFilepath()
err = os.MkdirAll(filepath.Dir(legacyMetadataFilepath), os.FileMode(0755))
assert.NoError(t, err)

err = ioutil.WriteFile(legacyMetadataFilepath, []byte{}, os.FileMode(0600))
assert.NoError(t, err)

ok, _ := exercise.HasLegacyMetadata()
assert.True(t, ok)
ok, _ = exercise.HasMetadata()
assert.False(t, ok)

status, err := exercise.MigrateLegacyMetadataFile()
assert.Equal(t, MigrationStatusMigrated, status)
assert.NoError(t, err)

ok, _ = exercise.HasLegacyMetadata()
assert.False(t, ok)
ok, _ = exercise.HasMetadata()
assert.True(t, ok)
}

func TestMigrateLegacyMetadataFileWithLegacyAndModern(t *testing.T) {
ws, err := ioutil.TempDir("", "fake-workspace")
defer os.RemoveAll(ws)
assert.NoError(t, err)

exercise := Exercise{Root: ws, Track: "bogus-track", Slug: "both-legacy-and-modern"}
metadataFilepath := exercise.MetadataFilepath()
legacyMetadataFilepath := exercise.LegacyMetadataFilepath()
err = os.MkdirAll(filepath.Dir(legacyMetadataFilepath), os.FileMode(0755))
assert.NoError(t, err)
err = os.MkdirAll(filepath.Dir(metadataFilepath), os.FileMode(0755))
assert.NoError(t, err)

err = ioutil.WriteFile(legacyMetadataFilepath, []byte{}, os.FileMode(0600))
assert.NoError(t, err)
err = ioutil.WriteFile(metadataFilepath, []byte{}, os.FileMode(0600))
assert.NoError(t, err)

ok, _ := exercise.HasLegacyMetadata()
assert.True(t, ok)
ok, _ = exercise.HasMetadata()
assert.True(t, ok)

status, err := exercise.MigrateLegacyMetadataFile()
assert.Equal(t, MigrationStatusRemoved, status)
assert.NoError(t, err)

ok, _ = exercise.HasLegacyMetadata()
assert.False(t, ok)
ok, _ = exercise.HasMetadata()
assert.True(t, ok)
}
Loading

0 comments on commit 5185642

Please sign in to comment.