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

     1  package lib
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/google/go-cmp/cmp"
     7  	"github.com/qri-io/qri/dsref"
     8  )
     9  
    10  func TestDatasetMethodsDiff(t *testing.T) {
    11  	tr := newTestRunner(t)
    12  	defer tr.Delete()
    13  
    14  	req := tr.Instance.Diff()
    15  	ds := tr.Instance.Dataset()
    16  	jobsOnePath := tr.MustWriteTmpFile(t, "jobs_by_automation_1.csv", jobsByAutomationData1)
    17  	jobsTwoPath := tr.MustWriteTmpFile(t, "jobs_by_automation_2.csv", jobsByAutomationData2)
    18  
    19  	djsOnePath := tr.MustWriteTmpFile(t, "djs_1.json", `{ "dj dj booth": { "rating": 1, "uses_soundcloud": true } }`)
    20  	djsTwoPath := tr.MustWriteTmpFile(t, "djs_2.json", `{ "DJ dj booth": { "rating": 1, "uses_soundcloud": true } }`)
    21  
    22  	initParams := &SaveParams{
    23  		Ref:      "me/jobs_ranked_by_automation_prob",
    24  		BodyPath: jobsOnePath,
    25  	}
    26  	ds1, err := ds.Save(tr.Ctx, initParams)
    27  	if err != nil {
    28  		t.Fatalf("couldn't save: %s", err.Error())
    29  	}
    30  
    31  	initParams = &SaveParams{
    32  		Ref:      "me/jobs_ranked_by_automation_prob",
    33  		BodyPath: jobsTwoPath,
    34  	}
    35  	ds2, err := ds.Save(tr.Ctx, initParams)
    36  	if err != nil {
    37  		t.Fatalf("couldn't save second revision: %s", err.Error())
    38  	}
    39  
    40  	dsRef1 := dsref.ConvertDatasetToVersionInfo(ds1).SimpleRef()
    41  	dsRef2 := dsref.ConvertDatasetToVersionInfo(ds2).SimpleRef()
    42  
    43  	good := []struct {
    44  		description string
    45  		Left, Right string
    46  		Selector    string
    47  		Stat        *DiffStat
    48  		DeltaLen    int
    49  	}{
    50  		{"two fully qualified references",
    51  			dsRef1.String(), dsRef2.String(),
    52  			"",
    53  			&DiffStat{Left: 209, Right: 209, LeftWeight: 5017, RightWeight: 5017, Inserts: 0, Updates: 0, Deletes: 0},
    54  			8,
    55  		},
    56  		{"fill left path from history",
    57  			dsRef2.Alias(), dsRef2.Alias(),
    58  			"",
    59  			&DiffStat{Left: 205, Right: 209, LeftWeight: 4920, RightWeight: 5017, Inserts: 19, Updates: 0, Deletes: 13},
    60  			10,
    61  		},
    62  		{"two local file paths",
    63  			"testdata/jobs_by_automation/body.csv", "testdata/jobs_by_automation_2/body.csv",
    64  			"",
    65  			&DiffStat{Left: 151, Right: 151, LeftWeight: 3757, RightWeight: 3757, Inserts: 1, Updates: 0, Deletes: 1},
    66  			30,
    67  		},
    68  		{"diff local csv & json file",
    69  			"testdata/now_tf/input.dataset.json", "testdata/jobs_by_automation/body.csv",
    70  			"",
    71  			&DiffStat{Left: 10, Right: 151, LeftWeight: 162, RightWeight: 3757, Inserts: 1, Updates: 0, Deletes: 1},
    72  			2,
    73  		},
    74  		{"case-sensitive key change",
    75  			djsOnePath, djsTwoPath,
    76  			"",
    77  			&DiffStat{Left: 4, Right: 4, LeftWeight: 18, RightWeight: 18, Inserts: 1, Updates: 0, Deletes: 1},
    78  			2,
    79  		},
    80  	}
    81  
    82  	// execute
    83  	for i, c := range good {
    84  		t.Run(c.description, func(t *testing.T) {
    85  			p := &DiffParams{
    86  				LeftSide:  c.Left,
    87  				RightSide: c.Right,
    88  				Selector:  c.Selector,
    89  			}
    90  			// If test has same two paths, we want the previous version compared to head
    91  			if p.LeftSide == p.RightSide {
    92  				p.UseLeftPrevVersion = true
    93  				p.RightSide = ""
    94  			}
    95  			res, err := req.Diff(tr.Ctx, p)
    96  			if err != nil {
    97  				t.Errorf("%d: \"%s\" error: %s", i, c.description, err.Error())
    98  				return
    99  			}
   100  
   101  			if diff := cmp.Diff(c.Stat, res.Stat); diff != "" {
   102  				t.Errorf("result mismatch (-want +got):\n%s", diff)
   103  			}
   104  
   105  			if len(res.Diff) != c.DeltaLen {
   106  				t.Errorf("%d: \"%s\" delta length mismatch. want: %d got: %d", i, c.description, c.DeltaLen, len(res.Diff))
   107  			}
   108  		})
   109  	}
   110  }
   111  
   112  const jobsByAutomationData1 = `
   113  rank,probability_of_automation,soc_code,job_title
   114  702,"0.99","41-9041","Telemarketers"
   115  701,"0.99","23-2093","Title Examiners, Abstractors, and Searchers"
   116  700,"0.99","51-6051","Sewers, Hand"
   117  699,"0.99","15-2091","Mathematical Technicians"
   118  698,"0.99","13-2053","Insurance Underwriters"
   119  697,"0.99","49-9064","Watch Repairers"
   120  696,"0.99","43-5011","Cargo and Freight Agents"
   121  695,"0.99","13-2082","Tax Preparers"
   122  694,"0.99","51-9151","Photographic Process Workers and Processing Machine Operators"
   123  693,"0.99","43-4141","New Accounts Clerks"
   124  692,"0.99","25-4031","Library Technicians"
   125  691,"0.99","43-9021","Data Entry Keyers"
   126  690,"0.98","51-2093","Timing Device Assemblers and Adjusters"
   127  689,"0.98","43-9041","Insurance Claims and Policy Processing Clerks"
   128  688,"0.98","43-4011","Brokerage Clerks"
   129  687,"0.98","43-4151","Order Clerks"
   130  686,"0.98","13-2072","Loan Officers"
   131  685,"0.98","13-1032","Insurance Appraisers, Auto Damage"
   132  684,"0.98","27-2023","Umpires, Referees, and Other Sports Officials"
   133  683,"0.98","43-3071","Tellers"
   134  682,"0.98","51-9194","Etchers and Engravers"
   135  681,"0.98","51-9111","Packaging and Filling Machine Operators and Tenders"
   136  680,"0.98","43-3061","Procurement Clerks"
   137  679,"0.98","43-5071","Shipping, Receiving, and Traffic Clerks"
   138  678,"0.98","51-4035","Milling and Planing Machine Setters, Operators, and Tenders, Metal and Plastic"
   139  677,"0.98","13-2041","Credit Analysts"
   140  676,"0.98","41-2022","Parts Salespersons"
   141  675,"0.98","13-1031","Claims Adjusters, Examiners, and Investigators"
   142  674,"0.98","53-3031","Driver/Sales Workers"
   143  673,"0.98","27-4013","Radio Operators"
   144  `
   145  
   146  const jobsByAutomationData2 = `
   147  rank,probability_of_automation,industry_code,job_name
   148  702,"0.99","41-9041","Telemarketers"
   149  701,"0.99","23-2093","Title Examiners, Abstractors, and Searchers"
   150  700,"0.99","51-6051","Sewers, Hand"
   151  699,"0.99","15-2091","Mathematical Technicians"
   152  698,"0.88","13-2053","Insurance Underwriters"
   153  697,"0.99","49-9064","Watch Repairers"
   154  696,"0.99","43-5011","Cargo and Freight Agents"
   155  695,"0.99","13-2082","Tax Preparers"
   156  694,"0.99","51-9151","Photographic Process Workers and Processing Machine Operators"
   157  693,"0.99","43-4141","New Accounts Clerks"
   158  692,"0.99","25-4031","Library Technicians"
   159  691,"0.99","43-9021","Data Entry Keyers"
   160  690,"0.98","51-2093","Timing Device Assemblers and Adjusters"
   161  689,"0.98","43-9041","Insurance Claims and Policy Processing Clerks"
   162  688,"0.98","43-4011","Brokerage Clerks"
   163  687,"0.98","43-4151","Order Clerks"
   164  686,"0.98","13-2072","Loan Officers"
   165  685,"0.98","13-1032","Insurance Appraisers, Auto Damage"
   166  684,"0.98","27-2023","Umpires, Referees, and Other Sports Officials"
   167  683,"0.98","43-3071","Tellers"
   168  682,"0.98","51-9194","Etchers and Engravers"
   169  681,"0.98","51-9111","Packaging and Filling Machine Operators and Tenders"
   170  680,"0.98","43-3061","Procurement Clerks"
   171  679,"0.98","43-5071","Shipping, Receiving, and Traffic Clerks"
   172  678,"0.98","51-4035","Milling and Planing Machine Setters, Operators, and Tenders, Metal and Plastic"
   173  677,"0.98","13-2041","Credit Analysts"
   174  676,"0.98","41-2022","Parts Salespersons"
   175  675,"0.98","13-1031","Claims Adjusters, Examiners, and Investigators"
   176  674,"0.98","53-3031","Driver/Sales Workers"
   177  673,"0.98","27-4013","Radio Operators"
   178  `
   179  
   180  // Test that we can compare bodies of different dataset revisions.
   181  func TestDiffPrevRevision(t *testing.T) {
   182  	run := newTestRunner(t)
   183  	defer run.Delete()
   184  
   185  	// Save three versions, then diff the last head against its previous version
   186  	run.MustSaveFromBody(t, "test_cities", "testdata/cities_2/body.csv")
   187  	run.MustSaveFromBody(t, "test_cities", "testdata/cities_2/body_more.csv")
   188  	run.MustSaveFromBody(t, "test_cities", "testdata/cities_2/body_even_more.csv")
   189  
   190  	output, err := run.Diff("me/test_cities", "", "body")
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  
   195  	// TODO(dustmop): Come up with a better way to represent this diff, that still looks nice when
   196  	// compared with cmp.Diff.
   197  	expect := `{"stat":{"leftNodes":36,"rightNodes":46,"leftWeight":510,"rightWeight":637,"inserts":4,"deletes":2},"diff":[[" ",0,["toronto",50000000,55.5,false]],[" ",1,["new york",8500000,44.4,true]],[" ",2,["los angeles",3990000,42.7,true]],["-",3,["chicago",300000,44.4,true]],["+",3,["dallas",1340000,30,true]],[" ",4,["chatham",35000,65.25,true]],[" ",5,null,[[" ",0,"mexico city"],["-",1,70000000],["+",1,80000000],[" ",2,28.6],[" ",3,false]]],[" ",6,["raleigh",250000,50.65,true]],["+",7,["paris",2100000,41.1,false]],["+",8,["london",8900000,36.5,false]]]}`
   198  
   199  	if diff := cmp.Diff(expect, output); diff != "" {
   200  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   201  	}
   202  }
   203  
   204  // Test that we can compare two different datasets
   205  func TestDiff(t *testing.T) {
   206  	run := newTestRunner(t)
   207  	defer run.Delete()
   208  
   209  	// Save a dataset with one version
   210  	run.MustSaveFromBody(t, "test_cities", "testdata/cities_2/body.csv")
   211  	// Save a different dataset with one version
   212  	run.MustSaveFromBody(t, "test_more", "testdata/cities_2/body_more.csv")
   213  
   214  	// Diff the heads
   215  	output, err := run.Diff("me/test_cities", "me/test_more", "")
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  
   220  	// TODO(dustmop): Come up with a better way to represent this diff, that still looks nice when
   221  	// compared with cmp.Diff.
   222  	// TODO(dustmop): Would be better if Diff only returned the changes, instead of things that
   223  	// stay the same, since the delta in this case is pretty small.
   224  	expect := `{"stat":{"leftNodes":103,"rightNodes":113,"leftWeight":3918,"rightWeight":4274,"inserts":52,"deletes":26},"diff":[["-","bodyPath","/mem/Qmc7AoCfFVW5xe8qhyjNYewSgBHFubp6yLM3mfBzQp7iTr"],["+","bodyPath","/mem/QmYuVj1JvALB9Au5DNcVxGLMcWCDBUfbKCN3QbpvissSC4"],[" ","commit",null,[[" ","author",{"id":"QmeL2mdVka1eahKENjehK6tBxkkpk5dNQ1qMcgWi7Hrb4B"}],["-","message","created dataset from body.csv"],["+","message","created dataset from body_more.csv"],["-","path","/mem/QmTKLXhW5CHD9a8o4UwWo6EJ4X7Y2h6h2jsDRKw5tU54jS"],["+","path","/mem/QmQRf5y4L6Hn37a1bf1nkWYDh9wUeUcs6XKoi5v2v2zKjV"],[" ","qri","cm:0"],["-","signature","fYfAWIzpiArVi5g+Ls9dA2KLdcSqbqqBQTF/QVFAVA4IZ01T3gJVwDot6scb7twaM6tX2uihynMz6n1xsZi/x7TZFg7VZlACV/RRG93iyW2OcaKlvXgzqgW4HpNvEdaUvN5l59nRtf10lfoL8jN1SVKI6vlhtZtT0ETLliNA83JEfBCwrWk0ftf/lKFBcbLUv0sI0x8km2gV7fTvAHOeTjiCO5Ya+2ID9AI8TN8AMEAltRkyEQMDankrHg8wEnOwadipZM2/qn0wsZae7b/n2T1JNJPs78tVjC95/7AW+JEqrlhtGpwqX2d5t4vbSMvC1vB/gf8nRwh9PVIaz9ZoTg=="],["+","signature","ibX3sNj08nbcKES2aGXkVk+ZMKgmZEdtnB8Q5be4v4y9DhRrRWUbR2R3XwOTs0vfv3wMcsHqp9jY/v7k/HNnNsCqg0PBiYGdXkvEWr2sjqJbG6A+0x5Bkqzkxf2FhrUqzqx4wMvJYZsgwSf7UDWNh/A/1wybt8jR7P2tT7IWLKbwaWJWhG5nk2NvlkL3nQIVLBApJaYCQhjolAp0n2q82jgD9x1dzs2qgV3oSyVqBdWCCg9GY/q9QuqmQHM/wyxJajXo8gGV0LDgPh39sZfqRqQjJ8koszCTiYl1b9np41GBFkKBn0KRIvSGJrE62NS87YkIi4wOib66XjoY8jaA9w=="],["-","timestamp","2001-01-01T01:01:01.000000001Z"],["+","timestamp","2001-01-01T01:02:01.000000001Z"],["-","title","created dataset from body.csv"],["+","title","created dataset from body_more.csv"]]],["-","id","xtxdndmgp56itafwfssw6nqrgy5fopj77loggh5vrr3cvok7yueq"],["+","id","gr3rhewayftulvahrh4kkahthz55jh545nxgh2lxikqi3psi3gla"],["-","path","/mem/QmbTMgT154t4NnP4H2FXBPcec7D85HrJKnBmZKWvRsYCtS"],["+","path","/mem/QmXQ6L58wzoG57QkLGp6iQtd24PGbnnRk9kPvaE1QPXzcq"],[" ","qri","ds:0"],[" ","stats",null,[["-","path","/mem/QmVvv9vBHLsYbDYzG1G931Pi58gTPdVE4hcUsxb6rAU8S9"],["+","path","/mem/QmcLRdnni9jpvhACHN9LmUsDuT2iiqpEhQ1ZqxdLK5ELr6"],[" ","qri","sa:0"],[" ","stats",null,[[" ",0,null,[["-","count",5],["+","count",7],[" ","frequencies",null,[[" ","chatham",1],[" ","chicago",1],["+","los angeles",1],["+","mexico city",1],[" ","new york",1],[" ","raleigh",1],[" ","toronto",1]]],["-","maxLength",8],["+","maxLength",11],[" ","minLength",7],[" ","type","string"],["-","unique",5],["+","unique",7]]],[" ",1,null,[["-","count",5],["+","count",7],[" ","histogram",null,[[" ","bins",null,[["+",0,35000],[" ",1,250000],["+",2,300000],["+",3,3990000],[" ",4,8500000],["+",5,50000000],["+",6,70000000],["+",7,70000001]]],[" ","frequencies",null,[["+",0,1],["+",1,1],["+",2,1],["+",3,1],["+",4,1],["+",5,1],["+",6,1]]]]],["-","max",50000000],["+","max",70000000],["-","mean",11817000],["+","mean",19010714.285714287],["-","median",300000],["+","median",3990000],[" ","min",35000],[" ","type","numeric"]]],[" ",2,null,[["-","count",5],["+","count",7],[" ","histogram",null,[[" ","bins",null,[["+",0,28.6],["+",1,42.7],["+",2,44.4],["+",3,50.65],[" ",4,55.5],["+",5,65.25],[" ",6,66.25]]],[" ","frequencies",null,[["+",0,1],["+",1,1],["+",2,2],["+",3,1],["+",4,1],["+",5,1]]]]],[" ","max",65.25],["-","mean",52.04],["+","mean",47.357142857142854],[" ","median",50.65],["-","min",44.4],["+","min",28.6],[" ","type","numeric"]]],[" ",3,null,[["-","count",5],["+","count",7],["-","falseCount",1],["+","falseCount",2],["-","trueCount",4],["+","trueCount",5],[" ","type","boolean"]]]]]]],[" ","structure",null,[["-","checksum","/mem/Qmc7AoCfFVW5xe8qhyjNYewSgBHFubp6yLM3mfBzQp7iTr"],["+","checksum","/mem/QmYuVj1JvALB9Au5DNcVxGLMcWCDBUfbKCN3QbpvissSC4"],[" ","depth",2],["-","entries",5],["+","entries",7],[" ","format","csv"],[" ","formatConfig",{"headerRow":true,"lazyQuotes":true}],["-","length",155],["+","length",217],["-","path","/mem/QmX3HjmvFGYXavQiPqpJvAZZ14J1DNPjCCGEzEy9NgZq2J"],["+","path","/mem/QmNpWHSFo8xwNQyamsaYAqUKu7Nzd3J1a4MukyNVf4J1xt"],[" ","qri","st:0"],[" ","schema",{"items":{"items":[{"title":"city","type":"string"},{"title":"pop","type":"integer"},{"title":"avg_age","type":"number"},{"title":"in_usa","type":"boolean"}],"type":"array"},"type":"array"}]]]]}`
   225  	if diff := cmp.Diff(expect, output); diff != "" {
   226  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   227  	}
   228  
   229  	// Diff the bodies
   230  	output, err = run.Diff("me/test_cities", "me/test_more", "body")
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  
   235  	expect = `{"stat":{"leftNodes":26,"rightNodes":36,"leftWeight":344,"rightWeight":510,"inserts":2},"diff":[[" ",0,["toronto",50000000,55.5,false]],[" ",1,["new york",8500000,44.4,true]],["+",2,["los angeles",3990000,42.7,true]],[" ",3,["chicago",300000,44.4,true]],[" ",4,["chatham",35000,65.25,true]],["+",5,["mexico city",70000000,28.6,false]],[" ",6,["raleigh",250000,50.65,true]]]}`
   236  	if diff := cmp.Diff(expect, output); diff != "" {
   237  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   238  	}
   239  }
   240  
   241  // Test that diffing a dataset with only one version produces an error
   242  func TestDiffOnlyOneRevision(t *testing.T) {
   243  	run := newTestRunner(t)
   244  	defer run.Delete()
   245  
   246  	run.MustSaveFromBody(t, "test_cities", "testdata/cities_2/body.csv")
   247  	_, err := run.Diff("me/test_cities", "", "body")
   248  	if err == nil {
   249  		t.Fatal("expected error, did not get one")
   250  	}
   251  	expect := `dataset has only one version, nothing to diff against`
   252  	if err.Error() != expect {
   253  		t.Errorf("expected error: %q, got: %q", expect, err)
   254  	}
   255  }
   256  
   257  // Test that we can compare csv files
   258  func TestDiffLocalCsvFiles(t *testing.T) {
   259  	run := newTestRunner(t)
   260  	defer run.Delete()
   261  
   262  	output, err := run.Diff("testdata/cities_2/body.csv", "testdata/cities_2/body_more.csv", "")
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  	expect := `{"stat":{"leftNodes":26,"rightNodes":36,"leftWeight":344,"rightWeight":510,"inserts":2},"schemaStat":{"leftNodes":5,"rightNodes":5,"leftWeight":41,"rightWeight":41},"schema":[[" ",0,"city"],[" ",1,"pop"],[" ",2,"avg_age"],[" ",3,"in_usa"]],"diff":[[" ",0,["toronto",50000000,55.5,false]],[" ",1,["new york",8500000,44.4,true]],["+",2,["los angeles",3990000,42.7,true]],[" ",3,["chicago",300000,44.4,true]],[" ",4,["chatham",35000,65.25,true]],["+",5,["mexico city",70000000,28.6,false]],[" ",6,["raleigh",250000,50.65,true]]]}`
   267  	if diff := cmp.Diff(expect, output); diff != "" {
   268  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   269  	}
   270  }
   271  
   272  // Test that we can compare json files
   273  func TestDiffLocalJsonFiles(t *testing.T) {
   274  	run := newTestRunner(t)
   275  	defer run.Delete()
   276  
   277  	output, err := run.Diff("../cmd/testdata/movies/body_two.json", "../cmd/testdata/movies/body_four.json", "")
   278  	if err != nil {
   279  		t.Fatal(err)
   280  	}
   281  	expect := `{"stat":{"leftNodes":7,"rightNodes":13,"leftWeight":161,"rightWeight":267,"inserts":2},"schemaStat":{"leftNodes":2,"rightNodes":2,"leftWeight":11,"rightWeight":11},"schema":[[" ","type","array"]],"diff":[[" ",0,["Avatar",178]],[" ",1,["Pirates of the Caribbean: At World's End",169]],["+",2,["Spectre",148]],["+",3,["The Dark Knight Rises",164]]]}`
   282  	if diff := cmp.Diff(expect, output); diff != "" {
   283  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   284  	}
   285  }
   286  
   287  func TestDiffErrors(t *testing.T) {
   288  	run := newTestRunner(t)
   289  	defer run.Delete()
   290  
   291  	// Save a dataset with one version
   292  	run.MustSaveFromBody(t, "test_cities", "testdata/cities_2/body.csv")
   293  
   294  	// Save a different dataset with one version
   295  	run.MustSaveFromBody(t, "test_more", "testdata/cities_2/body_more.csv")
   296  
   297  	// Error to compare a dataset ref to a file
   298  	_, err := run.Diff("me/test_cities", "testdata/cities_2/body_even_more.csv", "")
   299  	expectErr := `cannot compare a file to dataset, must compare similar things`
   300  	if diff := cmp.Diff(expectErr, errorMessage(err)); diff != "" {
   301  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   302  	}
   303  
   304  	// Error to only set left-side
   305  	_, err = run.DiffWithParams(&DiffParams{
   306  		LeftSide: "me/test_cities",
   307  	})
   308  	expectErr = `invalid parameters to diff`
   309  	if diff := cmp.Diff(expectErr, errorMessage(err)); diff != "" {
   310  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   311  	}
   312  
   313  	// Error to set left-side with both WorkingDir and UseLeftPrevVersion
   314  	_, err = run.DiffWithParams(&DiffParams{
   315  		LeftSide:           "me/test_cities",
   316  		WorkingDir:         "workdir",
   317  		UseLeftPrevVersion: true,
   318  	})
   319  	expectErr = `cannot use both previous version and working directory`
   320  	if diff := cmp.Diff(expectErr, errorMessage(err)); diff != "" {
   321  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   322  	}
   323  
   324  	// Error to set left-side and right-side with WorkingDir
   325  	_, err = run.DiffWithParams(&DiffParams{
   326  		LeftSide:   "me/test_cities",
   327  		RightSide:  "me/test_more",
   328  		WorkingDir: "workdir",
   329  	})
   330  	expectErr = `cannot use working directory when comparing two sources`
   331  	if diff := cmp.Diff(expectErr, errorMessage(err)); diff != "" {
   332  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   333  	}
   334  
   335  	// Error to set left-side and right-side with UseLeftPrevVersion
   336  	_, err = run.DiffWithParams(&DiffParams{
   337  		LeftSide:           "me/test_cities",
   338  		RightSide:          "me/test_more",
   339  		UseLeftPrevVersion: true,
   340  	})
   341  	expectErr = `cannot use previous version when comparing two sources`
   342  	if diff := cmp.Diff(expectErr, errorMessage(err)); diff != "" {
   343  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   344  	}
   345  
   346  	// Error to use a selector for a field that doesn't exist
   347  	_, err = run.Diff("me/test_cities", "me/test_more", "meta")
   348  	expectErr = `component "meta" not found`
   349  	if diff := cmp.Diff(expectErr, errorMessage(err)); diff != "" {
   350  		t.Errorf("output mismatch (-want +got):\n%s", diff)
   351  	}
   352  }
   353  
   354  // TODO(dustmop): Test comparing a dataset in FSI, with a modification in the working directory
   355  // TODO(dustmop): Test comparing a dataset in FSI, using selector
   356  
   357  func errorMessage(err error) string {
   358  	if err == nil {
   359  		return ""
   360  	}
   361  	return err.Error()
   362  }