github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/cmd/diff_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/qri-io/dataset/dstest"
    10  	qerr "github.com/qri-io/qri/errors"
    11  )
    12  
    13  func TestDiffComplete(t *testing.T) {
    14  	run := NewTestRunner(t, "test_peer_diff_complete", "qri_test_diff_complete")
    15  	defer run.Delete()
    16  
    17  	ctx, cancel := context.WithCancel(context.Background())
    18  	defer cancel()
    19  
    20  	f, err := NewTestFactory(ctx)
    21  	if err != nil {
    22  		t.Errorf("error creating new test factory: %s", err)
    23  		return
    24  	}
    25  
    26  	cases := []struct {
    27  		args []string
    28  		err  string
    29  	}{
    30  		{[]string{}, ""},
    31  		{[]string{"one arg"}, ""},
    32  		{[]string{"one arg", "two args"}, ""},
    33  	}
    34  
    35  	for i, c := range cases {
    36  		opt := &DiffOptions{
    37  			IOStreams: run.Streams,
    38  		}
    39  
    40  		opt.Complete(f, c.args)
    41  
    42  		if c.err != run.ErrStream.String() {
    43  			t.Errorf("case %d, error mismatch. Expected: %q, Got: %q", i, c.err, run.ErrStream.String())
    44  			run.IOReset()
    45  			continue
    46  		}
    47  
    48  		if opt.inst == nil {
    49  			t.Errorf("case %d, opt.inst not set.", i)
    50  			run.IOReset()
    51  			continue
    52  		}
    53  		run.IOReset()
    54  	}
    55  }
    56  
    57  func TestDiffRun(t *testing.T) {
    58  	run := NewTestRunner(t, "test_peer_qri_test_dag_info", "qri_test_dag_info")
    59  	defer run.Delete()
    60  
    61  	ctx, cancel := context.WithCancel(context.Background())
    62  	defer cancel()
    63  
    64  	f, err := NewTestFactory(ctx)
    65  	if err != nil {
    66  		t.Errorf("error creating new test factory: %s", err)
    67  		return
    68  	}
    69  
    70  	good := []struct {
    71  		description string
    72  		opt         *DiffOptions
    73  		stdout      string
    74  	}{
    75  		{"diff two dataset metas",
    76  			&DiffOptions{
    77  				Refs:     NewListOfRefSelects([]string{"me/movies", "me/cities"}),
    78  				Selector: "meta",
    79  			},
    80  			`0 elements. 2 inserts. 2 deletes.
    81  
    82  -path: "/mem/QmZQNhYYVRx8LyMmPV9mqzVZVEeZKpso4Ywu7nwyWvT4X4"
    83  +path: "/mem/QmWX9MV7ms5QXVGt26gXAbp5z8TdfamUgVBdzxSqhWhPzV"
    84   qri: "md:0"
    85  -title: "example movie data"
    86  +title: "example city data"
    87  `,
    88  		},
    89  		{"diff json output",
    90  			&DiffOptions{
    91  				Refs:     NewListOfRefSelects([]string{"me/movies", "me/cities"}),
    92  				Selector: "meta",
    93  				Format:   "json",
    94  			},
    95  			`{"stat":{"leftNodes":4,"rightNodes":4,"leftWeight":147,"rightWeight":145,"inserts":2,"deletes":2},"diff":[["-","path","/mem/QmZQNhYYVRx8LyMmPV9mqzVZVEeZKpso4Ywu7nwyWvT4X4"],["+","path","/mem/QmWX9MV7ms5QXVGt26gXAbp5z8TdfamUgVBdzxSqhWhPzV"],[" ","qri","md:0"],["-","title","example movie data"],["+","title","example city data"]]}
    96  `,
    97  		},
    98  	}
    99  
   100  	for _, c := range good {
   101  		t.Run(c.description, func(t *testing.T) {
   102  			inst, err := f.Instance()
   103  			if err != nil {
   104  				t.Fatalf("case %s, error creating inst: %s", c.description, err)
   105  			}
   106  
   107  			opt := c.opt
   108  			opt.IOStreams = run.Streams
   109  			opt.inst = inst
   110  
   111  			if err = opt.Run(); err != nil {
   112  				t.Fatalf("case %s unexpected error: %s", c.description, err)
   113  			}
   114  
   115  			if diff := cmp.Diff(c.stdout, run.OutStream.String()); diff != "" {
   116  				t.Errorf("output mismatch (-want +got):\n%s", diff)
   117  			}
   118  
   119  			run.IOReset()
   120  		})
   121  	}
   122  
   123  	bad := []struct {
   124  		opt *DiffOptions
   125  		err string
   126  	}{
   127  		{
   128  			&DiffOptions{},
   129  			"nothing to diff",
   130  		},
   131  	}
   132  
   133  	for _, c := range bad {
   134  		inst, err := f.Instance()
   135  		if err != nil {
   136  			t.Errorf("case %s, error creating instance: %s", c.err, err)
   137  			continue
   138  		}
   139  
   140  		opt := c.opt
   141  		opt.Refs = NewListOfRefSelects([]string{})
   142  		opt.IOStreams = run.Streams
   143  		opt.inst = inst
   144  
   145  		err = opt.Run()
   146  
   147  		if err == nil {
   148  			t.Errorf("expected: %q, got no error", c.err)
   149  			run.IOReset()
   150  			continue
   151  		}
   152  
   153  		var qerror qerr.Error
   154  		if errors.As(err, &qerror) {
   155  			if qerror.Message() != c.err {
   156  				t.Errorf("qri error mismatch. expected:\n%q\n,got:\n%q", c.err, qerror.Message())
   157  			}
   158  		} else if c.err != err.Error() {
   159  			t.Errorf("error mismatch. expected: %q, got: %q", c.err, err.Error())
   160  		}
   161  		run.IOReset()
   162  	}
   163  }
   164  
   165  // Test that we can compare bodies of different dataset revisions.
   166  func TestDiffPrevRevision(t *testing.T) {
   167  	run := NewTestRunner(t, "test_peer_diff_prev_revisions", "qri_test_diff_revisions")
   168  	defer run.Delete()
   169  
   170  	// Save three versions, then diff the last two
   171  	run.MustExec(t, "qri save --body=testdata/movies/body_ten.csv me/test_movies")
   172  	run.MustExec(t, "qri save --body=testdata/movies/body_twenty.csv me/test_movies")
   173  	run.MustExec(t, "qri save --body=testdata/movies/body_thirty.csv me/test_movies")
   174  	output := run.MustExec(t, "qri diff body me/test_movies")
   175  
   176  	expect := `+30 elements. 10 inserts. 0 deletes.
   177  
   178   0: ["Avatar ",178]
   179   1: ["Pirates of the Caribbean: At World's End ",169]
   180   2: ["Spectre ",148]
   181   3: ["The Dark Knight Rises ",164]
   182   4: ["Star Wars: Episode VII - The Force Awakens             ",""]
   183   5: ["John Carter ",132]
   184   6: ["Spider-Man 3 ",156]
   185   7: ["Tangled ",100]
   186   8: ["Avengers: Age of Ultron ",141]
   187   9: ["Harry Potter and the Half-Blood Prince ",153]
   188   10: ["Batman v Superman: Dawn of Justice ",183]
   189   11: ["Superman Returns ",169]
   190   12: ["Quantum of Solace ",106]
   191   13: ["Pirates of the Caribbean: Dead Man's Chest ",151]
   192   14: ["The Lone Ranger ",150]
   193   15: ["Man of Steel ",143]
   194   16: ["The Chronicles of Narnia: Prince Caspian ",150]
   195   17: ["The Avengers ",173]
   196  +18: ["Dragonfly ",104]
   197  +19: ["The Black Dahlia ",121]
   198  +20: ["Flyboys ",140]
   199  +21: ["The Last Castle ",131]
   200  +22: ["Supernova ",91]
   201  +23: ["Winter's Tale ",118]
   202  +24: ["The Mortal Instruments: City of Bones ",130]
   203  +25: ["Meet Dave ",90]
   204  +26: ["Dark Water ",103]
   205  +27: ["Edtv ",122]
   206  `
   207  	if diff := cmp.Diff(expect, output); diff != "" {
   208  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   209  	}
   210  }
   211  
   212  // Test that diff works using the name of a component file to mean a selector for that component
   213  func TestDiffKnownFilenameComponent(t *testing.T) {
   214  	if err := confirmQriNotRunning(); err != nil {
   215  		t.Skip(err.Error())
   216  	}
   217  
   218  	run := NewTestRunner(t, "test_peer_diff_known_filename_component", "qri_test_diff_revisions")
   219  	defer run.Delete()
   220  
   221  	// Save two versions with a change to the structure
   222  	run.MustExec(t, "qri save --body=testdata/movies/body_ten.csv --file=testdata/movies/structure_override.json me/test_movies")
   223  	run.MustExec(t, "qri save --file=testdata/movies/structure_rename.json me/test_movies")
   224  
   225  	// Diff the structure, using the name of the component file
   226  	output := run.MustExec(t, "qri diff structure.json me/test_movies")
   227  
   228  	expect := dstest.Template(t, `0 elements. 2 inserts. 2 deletes.
   229  
   230   checksum: "{{ .checksum }}"
   231   depth: 2
   232   entries: 8
   233   errCount: 1
   234   format: "csv"
   235   formatConfig: {"headerRow":true,"lazyQuotes":false}
   236   length: 224
   237  -path: "{{ .leftPath }}"
   238  +path: "{{ .rightPath }}"
   239   qri: "st:0"
   240   schema: 
   241     items: 
   242       items: 
   243         0: 
   244          -title: "name"
   245          +title: "title"
   246           type: "string"
   247         1: {"title":"duration","type":"integer"}
   248     type: "array"
   249  `, map[string]string{
   250  		"checksum":  "/ipfs/QmXhsUK6vGZrqarhw9Z8RCXqhmEpvtVByKtaYVarbDZ5zn",
   251  		"leftPath":  "/ipfs/QmPPcg1nxCaBQ25J1DX9VrQYpk7pyAcQnLqavR8wvvFXuA",
   252  		"rightPath": "/ipfs/QmR4jah2qY6VWLVwVFi1CDttYMjWSB4MEovKPx9x2C5NxT",
   253  	})
   254  
   255  	if diff := cmp.Diff(expect, output); diff != "" {
   256  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   257  	}
   258  }