Skip to content

Commit

Permalink
feat(status): Status at a specific dataset version, historical changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dustmop committed Jul 25, 2019
1 parent ac863bf commit cbc108c
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 69 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ jobs:
environment:
GO111MODULE: "on"
GOPROXY: "https://proxy.golang.org"
GOSUMDB: "off"
environment:
TEST_RESULTS: /tmp/test-results
# resource_class requires a paid circleci plan:
Expand Down
123 changes: 123 additions & 0 deletions cmd/fsi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ package cmd

import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/qri-io/dataset/dsfs"
)

// Test using "init" to create a new linked directory, using status to see the added files,
Expand Down Expand Up @@ -517,6 +520,126 @@ fix these problems before saving this dataset
}
}

// Test status at specific versions
func TestStatusAtVersion(t *testing.T) {
if err := confirmQriNotRunning(); err != nil {
t.Skip(err.Error())
}

// To keep hashes consistent, artificially specify the timestamp by overriding
// the dsfs.Timestamp func
prev := dsfs.Timestamp
defer func() { dsfs.Timestamp = prev }()
dsfs.Timestamp = func() time.Time { return time.Date(2001, 01, 01, 01, 01, 01, 01, time.UTC) }

r := NewTestRepoRoot(t, "qri_test_status_at_version")
defer r.Delete()

ctx, done := context.WithCancel(context.Background())
defer done()

// Add a version with just a body
cmdR := r.CreateCommandRunner(ctx)
err := executeCommand(cmdR, "qri save --body=testdata/movies/body_two.json me/status_ver")
if err != nil {
t.Fatalf(err.Error())
}
ref1 := parseRefFromSave(r.GetOutput())

// Add a meta
cmdR = r.CreateCommandRunner(ctx)
err = executeCommand(cmdR, "qri save --file=testdata/movies/meta_override.yaml me/status_ver")
if err != nil {
t.Fatalf(err.Error())
}
ref2 := parseRefFromSave(r.GetOutput())

// Change the meta
cmdR = r.CreateCommandRunner(ctx)
err = executeCommand(cmdR, "qri save --file=testdata/movies/meta_another.yaml me/status_ver")
if err != nil {
t.Fatalf(err.Error())
}
ref3 := parseRefFromSave(r.GetOutput())

// Change the body
cmdR = r.CreateCommandRunner(ctx)
err = executeCommand(cmdR, "qri save --body=testdata/movies/body_four.json me/status_ver")
if err != nil {
t.Fatalf(err.Error())
}
ref4 := parseRefFromSave(r.GetOutput())

// Status for the first version of the dataset, both body and schema were added.
cmdR = r.CreateCommandRunner(ctx)
err = executeCommand(cmdR, fmt.Sprintf("qri status %s", ref1))
if err != nil {
t.Fatalf(err.Error())
}

output := r.GetOutput()
expect := ` schema: add
body: add
`
if diff := cmpTextLines(expect, output); diff != "" {
t.Errorf("qri status (-want +got):\n%s", diff)
}

// Status for the second version, meta added.
cmdR = r.CreateCommandRunner(ctx)
err = executeCommand(cmdR, fmt.Sprintf("qri status %s", ref2))
if err != nil {
t.Fatalf(err.Error())
}

output = r.GetOutput()
expect = ` meta: add
schema: unmodified
body: unmodified
`
if diff := cmpTextLines(expect, output); diff != "" {
t.Errorf("qri status (-want +got):\n%s", diff)
}

// Status for the third version, meta modified.
cmdR = r.CreateCommandRunner(ctx)
err = executeCommand(cmdR, fmt.Sprintf("qri status %s", ref3))
if err != nil {
t.Fatalf(err.Error())
}

output = r.GetOutput()
expect = ` meta: modified
schema: unmodified
body: unmodified
`
if diff := cmpTextLines(expect, output); diff != "" {
t.Errorf("qri status (-want +got):\n%s", diff)
}

// Status for the fourth version, body modified.
cmdR = r.CreateCommandRunner(ctx)
err = executeCommand(cmdR, fmt.Sprintf("qri status %s", ref4))
if err != nil {
t.Fatalf(err.Error())
}

output = r.GetOutput()
expect = ` meta: unmodified
schema: unmodified
body: modified
`
if diff := cmpTextLines(expect, output); diff != "" {
t.Errorf("qri status (-want +got):\n%s", diff)
}
}

func parseRefFromSave(output string) string {
pos := strings.Index(output, "saved: ")
ref := output[pos+7:]
return strings.TrimSpace(ref)
}

func cmpTextLines(left, right string) string {
lside := strings.Split(left, "\n")
rside := strings.Split(right, "\n")
Expand Down
11 changes: 4 additions & 7 deletions cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func NewStatusCommand(f Factory, ioStreams ioes.IOStreams) *cobra.Command {
return err
}
if !o.Refs.IsLinked() {
return o.RunForeign()
return o.RunAtVersion()
}
return o.Run()
},
Expand Down Expand Up @@ -96,11 +96,8 @@ func (o *StatusOptions) Run() (err error) {
return nil
}

// RunForeign returns status on a non-fsi-linked reference
func (o *StatusOptions) RunForeign() (err error) {
printInfo(o.ErrOut, "using foreign dataset reference: %s", o.Refs.Ref())
printRefSelect(o.ErrOut, o.Refs)

// RunAtVersion displays status for a reference at a specific version
func (o *StatusOptions) RunAtVersion() (err error) {
res := []lib.StatusItem{}
ref := o.Refs.Ref()
if err := o.FSIMethods.StoredStatus(&ref, &res); err != nil {
Expand All @@ -109,7 +106,7 @@ func (o *StatusOptions) RunForeign() (err error) {
}

for _, si := range res {
printInfo(o.Out, fmt.Sprintf(" %s", si.Component))
printInfo(o.Out, fmt.Sprintf(" %s: %s", si.Component, si.Type))
}

return nil
Expand Down
6 changes: 6 additions & 0 deletions cmd/testdata/movies/body_four.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
["Avatar", 178],
["Pirates of the Caribbean: At World's End", 169],
["Spectre" ,148],
["The Dark Knight Rises" ,164]
]
2 changes: 2 additions & 0 deletions cmd/testdata/movies/meta_another.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
qri: md:0
title: yet another title
38 changes: 19 additions & 19 deletions fsi/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ var (
// a map of component names to the files they came from. Files can be specified
// in either JSON or YAML format. It is an error to specify any component more
// than once
func ReadDir(dir string) (ds *dataset.Dataset, mapping map[string]string, problems map[string]string, err error) {
mapping = map[string]string{}
func ReadDir(dir string) (ds *dataset.Dataset, fileMap map[string]string, problems map[string]string, err error) {
fileMap = map[string]string{}
ds = &dataset.Dataset{}
schema := map[string]interface{}{}
problems = nil
Expand All @@ -58,14 +58,14 @@ func ReadDir(dir string) (ds *dataset.Dataset, mapping map[string]string, proble
".yml": newYAMLDecoder,
}

addMapping := func(cmpName, path string) error {
if cmpPath, exists := mapping[cmpName]; exists {
addFile := func(cmpName, path string) error {
if cmpPath, exists := fileMap[cmpName]; exists {
cmpPath = filepath.Base(cmpPath)
path = filepath.Base(path)
return fmt.Errorf(`%s is defined in two places: %s and %s. please remove one`, cmpName, cmpPath, path)
}

mapping[cmpName] = path
fileMap[cmpName] = path
return nil
}

Expand All @@ -76,16 +76,16 @@ func ReadDir(dir string) (ds *dataset.Dataset, mapping map[string]string, proble
}
if _, err = os.Stat(filepath.Join(dir, "body.json")); !os.IsNotExist(err) {
if bodyFormat == "csv" {
return ds, mapping, problems, fmt.Errorf("body.csv and body.json both exist")
return ds, fileMap, problems, fmt.Errorf("body.csv and body.json both exist")
}
bodyFormat = "json"
}

bodyFilename := ""
if bodyFormat != "" {
bodyFilename = fmt.Sprintf("body.%s", bodyFormat)
if err = addMapping(componentNameBody, bodyFilename); err != nil {
return ds, mapping, problems, err
if err = addFile(componentNameBody, bodyFilename); err != nil {
return ds, fileMap, problems, err
}
if ds.BodyPath == "" {
ds.BodyPath = filepath.Join(dir, bodyFilename)
Expand All @@ -108,45 +108,45 @@ func ReadDir(dir string) (ds *dataset.Dataset, mapping map[string]string, proble
problems[cmpName] = filename
continue
}
if err = addMapping(cmpName, path); err != nil {
return ds, mapping, problems, err
if err = addFile(cmpName, path); err != nil {
return ds, fileMap, problems, err
}
}

switch cmpName {
case componentNameDataset:
if ds.Commit != nil {
if err = addMapping(componentNameCommit, path); err != nil {
if err = addFile(componentNameCommit, path); err != nil {
return
}
}
if ds.Meta != nil {
if err = addMapping(componentNameMeta, path); err != nil {
if err = addFile(componentNameMeta, path); err != nil {
return
}
}
if ds.Structure != nil {
if err = addMapping(componentNameStructure, path); err != nil {
if err = addFile(componentNameStructure, path); err != nil {
return
}
if ds.Structure.Schema != nil {
if err = addMapping(componentNameSchema, path); err != nil {
if err = addFile(componentNameSchema, path); err != nil {
return
}
}
}
if ds.Viz != nil {
if err = addMapping(componentNameViz, path); err != nil {
if err = addFile(componentNameViz, path); err != nil {
return
}
}
if ds.Transform != nil {
if err = addMapping(componentNameTransform, path); err != nil {
if err = addFile(componentNameTransform, path); err != nil {
return
}
}
if ds.Body != nil {
if err = addMapping(componentNameBody, path); err != nil {
if err = addFile(componentNameBody, path); err != nil {
return
}
}
Expand Down Expand Up @@ -178,11 +178,11 @@ func ReadDir(dir string) (ds *dataset.Dataset, mapping map[string]string, proble
}
}

if len(mapping) == 0 {
if len(fileMap) == 0 {
err = ErrNoDatasetFiles
}

return ds, mapping, problems, err
return ds, fileMap, problems, err
}

type decoderFactory func(io.Reader) decoder
Expand Down
Loading

0 comments on commit cbc108c

Please sign in to comment.