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 }