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

     1  package lib
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strconv"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	"github.com/qri-io/dataset/dstest"
    13  	"github.com/qri-io/qri/base/dsfs"
    14  	"github.com/qri-io/qri/base/params"
    15  	testcfg "github.com/qri-io/qri/config/test"
    16  	"github.com/qri-io/qri/dsref"
    17  	"github.com/qri-io/qri/event"
    18  	"github.com/qri-io/qri/p2p"
    19  	p2ptest "github.com/qri-io/qri/p2p/test"
    20  	reporef "github.com/qri-io/qri/repo/ref"
    21  	testrepo "github.com/qri-io/qri/repo/test"
    22  )
    23  
    24  func TestDatasetRequestsList(t *testing.T) {
    25  	ctx, done := context.WithCancel(context.Background())
    26  	defer done()
    27  
    28  	var (
    29  		movies, counter, cities, craigslist, sitemap dsref.VersionInfo
    30  	)
    31  
    32  	mr, err := testrepo.NewTestRepo()
    33  	if err != nil {
    34  		t.Fatalf("error allocating test repo: %s", err)
    35  		return
    36  	}
    37  
    38  	refs, err := mr.References(0, 30)
    39  	if err != nil {
    40  		t.Fatalf("error getting namespace: %s", err)
    41  	}
    42  
    43  	node, err := p2p.NewQriNode(mr, testcfg.DefaultP2PForTesting(), event.NilBus, nil)
    44  	if err != nil {
    45  		t.Fatal(err)
    46  	}
    47  
    48  	inst := NewInstanceFromConfigAndNode(ctx, testcfg.DefaultConfigForTesting(), node)
    49  
    50  	for _, ref := range refs {
    51  		dr := reporef.ConvertToVersionInfo(&ref)
    52  		switch dr.Name {
    53  		case "movies":
    54  			movies = dr
    55  		case "counter":
    56  			counter = dr
    57  		case "cities":
    58  			cities = dr
    59  		case "craigslist":
    60  			craigslist = dr
    61  		case "sitemap":
    62  			sitemap = dr
    63  		}
    64  	}
    65  
    66  	cases := []struct {
    67  		description string
    68  		p           *CollectionListParams
    69  		res         []dsref.VersionInfo
    70  		err         string
    71  	}{
    72  		{"list datasets - empty (default)", &CollectionListParams{}, []dsref.VersionInfo{cities, counter, craigslist, movies, sitemap}, ""},
    73  		{"list datasets - weird", &CollectionListParams{List: params.List{Limit: -33, Offset: -50}}, []dsref.VersionInfo{}, "limit of -33 is out of bounds"},
    74  		{"list datasets - happy path", &CollectionListParams{List: params.List{Limit: 30, Offset: 0}}, []dsref.VersionInfo{cities, counter, craigslist, movies, sitemap}, ""},
    75  		{"list datasets - limit 2 offset 0", &CollectionListParams{List: params.List{Limit: 2, Offset: 0}}, []dsref.VersionInfo{cities, counter}, ""},
    76  		{"list datasets - limit 2 offset 2", &CollectionListParams{List: params.List{Limit: 2, Offset: 2}}, []dsref.VersionInfo{craigslist, movies}, ""},
    77  		{"list datasets - limit 2 offset 4", &CollectionListParams{List: params.List{Limit: 2, Offset: 4}}, []dsref.VersionInfo{sitemap}, ""},
    78  		{"list datasets - limit 2 offset 5", &CollectionListParams{List: params.List{Limit: 2, Offset: 5}}, []dsref.VersionInfo{}, ""},
    79  		{"list datasets - order by timestamp", &CollectionListParams{List: params.List{OrderBy: params.OrderBy{{Key: "timestamp", Direction: params.OrderDESC}}, Limit: 30, Offset: 0}}, []dsref.VersionInfo{cities, counter, craigslist, movies, sitemap}, ""},
    80  		{"list datasets - username 'me'", &CollectionListParams{Username: "me", List: params.List{OrderBy: params.OrderBy{{Key: "timestamp", Direction: params.OrderDESC}}, Limit: 30, Offset: 0}}, []dsref.VersionInfo{cities, counter, craigslist, movies, sitemap}, ""},
    81  		// TODO: re-enable {&CollectionListParams{List: params.List {OrderBy: params.OrderBy{{Key: "name", Direction: OrderASC }}, Limit: 30, Offset: 0}}, []*dsref.VersionInfo{cities, counter, movies}, ""}},
    82  	}
    83  
    84  	for _, c := range cases {
    85  		got, _, err := inst.Collection().List(ctx, c.p)
    86  
    87  		if !(err == nil && c.err == "" || err != nil && err.Error() == c.err) {
    88  			t.Errorf("case '%s' error mismatch: expected: %s, got: %s", c.description, c.err, err)
    89  			continue
    90  		}
    91  
    92  		if c.err == "" && c.res != nil {
    93  			if len(c.res) != len(got) {
    94  				t.Errorf("case '%s' response length mismatch. expected %d, got: %d", c.description, len(c.res), len(got))
    95  				continue
    96  			}
    97  
    98  			for j, expect := range c.res {
    99  				if err := compareVersionInfoAsSimple(expect, got[j]); err != nil {
   100  					t.Errorf("case '%s' expected dataset error. index %d mismatch: %s", c.description, j, err.Error())
   101  					continue
   102  				}
   103  			}
   104  		}
   105  	}
   106  }
   107  
   108  func compareVersionInfoAsSimple(a, b dsref.VersionInfo) error {
   109  	if a.ProfileID != b.ProfileID {
   110  		return fmt.Errorf("PeerID mismatch. %s != %s", a.ProfileID, b.ProfileID)
   111  	}
   112  	if a.Username != b.Username {
   113  		return fmt.Errorf("Username mismatch. %s != %s", a.Username, b.Username)
   114  	}
   115  	if a.Name != b.Name {
   116  		return fmt.Errorf("Name mismatch. %s != %s", a.Name, b.Name)
   117  	}
   118  	if a.Path != b.Path {
   119  		return fmt.Errorf("Path mismatch. %s != %s", a.Path, b.Path)
   120  	}
   121  	return nil
   122  }
   123  
   124  func TestGetFromCollection(t *testing.T) {
   125  	tr := newTestRunner(t)
   126  	defer tr.Delete()
   127  
   128  	// Save a dataset with a body
   129  	_, err := tr.SaveWithParams(&SaveParams{
   130  		Ref:      "me/cities_ds",
   131  		BodyPath: "testdata/cities_2/body.csv",
   132  	})
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	// get from the repo
   138  	ds := tr.MustGet(t, "me/cities_ds")
   139  	expect := dsref.ConvertDatasetToVersionInfo(ds)
   140  	pro, err := tr.Instance.activeProfile(tr.Ctx)
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	expect.ProfileID = pro.ID.Encode()
   145  	expect.CommitCount = 1
   146  
   147  	// fetch from the collection
   148  	got, err := tr.Instance.Collection().Get(tr.Ctx, &CollectionGetParams{Ref: "me/cities_ds"})
   149  	if err != nil {
   150  		t.Fatalf("error getting from collection by ref: %s", err)
   151  	}
   152  
   153  	if diff := cmp.Diff(expect, *got); diff != "" {
   154  		t.Errorf("get from collection mistmatch (-want +got):\n%s", diff)
   155  	}
   156  
   157  	got, err = tr.Instance.Collection().Get(tr.Ctx, &CollectionGetParams{InitID: expect.InitID})
   158  	if err != nil {
   159  		t.Fatalf("error getting from collection by initID: %s", err)
   160  	}
   161  
   162  	if diff := cmp.Diff(expect, *got); diff != "" {
   163  		t.Errorf("get from collection mistmatch (-want +got):\n%s", diff)
   164  	}
   165  }
   166  
   167  func TestDatasetRequestsListP2p(t *testing.T) {
   168  	ctx, done := context.WithCancel(context.Background())
   169  	defer done()
   170  
   171  	// Matches what is used to generated test peers.
   172  	datasets := []string{"movies", "cities", "counter", "craigslist", "sitemap"}
   173  
   174  	factory := p2ptest.NewTestNodeFactory(p2p.NewTestableQriNode)
   175  	testPeers, err := p2ptest.NewTestNetwork(ctx, factory, 5)
   176  	if err != nil {
   177  		t.Errorf("error creating network: %s", err.Error())
   178  		return
   179  	}
   180  
   181  	if err := p2ptest.ConnectNodes(ctx, testPeers); err != nil {
   182  		t.Errorf("error connecting peers: %s", err.Error())
   183  	}
   184  
   185  	// Convert from test nodes to non-test nodes.
   186  	peers := make([]*p2p.QriNode, len(testPeers))
   187  	for i, node := range testPeers {
   188  		peers[i] = node.(*p2p.QriNode)
   189  	}
   190  
   191  	var wg sync.WaitGroup
   192  	for _, p1 := range peers {
   193  		wg.Add(1)
   194  		go func(node *p2p.QriNode) {
   195  			defer wg.Done()
   196  
   197  			inst := NewInstanceFromConfigAndNode(ctx, testcfg.DefaultConfigForTesting(), node)
   198  			p := &CollectionListParams{List: params.List{Limit: 30, Offset: 0}}
   199  			res, _, err := inst.Collection().List(ctx, p)
   200  			if err != nil {
   201  				t.Errorf("error listing dataset: %s", err.Error())
   202  			}
   203  			// Get number from end of peername, use that to find dataset name.
   204  			profile := node.Repo.Profiles().Owner(ctx)
   205  			num := profile.Peername[len(profile.Peername)-1:]
   206  			index, _ := strconv.ParseInt(num, 10, 32)
   207  			expect := datasets[index]
   208  
   209  			if res[0].Name != expect {
   210  				t.Errorf("dataset %s mismatch: %s", res[0].Name, expect)
   211  			}
   212  		}(p1)
   213  	}
   214  
   215  	wg.Wait()
   216  }
   217  
   218  func TestListRawRefs(t *testing.T) {
   219  	ctx, done := context.WithCancel(context.Background())
   220  	defer done()
   221  
   222  	// TODO(dlong): Put a TestRunner instance here
   223  
   224  	// to keep hashes consistent, artificially specify the timestamp by overriding
   225  	// the dsfs.Timestamp func
   226  	prev := dsfs.Timestamp
   227  	defer func() { dsfs.Timestamp = prev }()
   228  	minute := 0
   229  	dsfs.Timestamp = func() time.Time {
   230  		minute++
   231  		return time.Date(2001, 01, 01, 01, minute, 01, 01, time.UTC)
   232  	}
   233  
   234  	mr, err := testrepo.NewTestRepo()
   235  	if err != nil {
   236  		t.Fatalf("error allocating test repo: %s", err.Error())
   237  	}
   238  	node, err := p2p.NewQriNode(mr, testcfg.DefaultP2PForTesting(), event.NilBus, nil)
   239  	if err != nil {
   240  		t.Fatal(err.Error())
   241  	}
   242  	inst := NewInstanceFromConfigAndNode(ctx, testcfg.DefaultConfigForTesting(), node)
   243  
   244  	text, err := inst.Collection().ListRawRefs(ctx, &EmptyParams{})
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  
   249  	expect := dstest.Template(t, `0 Peername:  peer
   250    ProfileID: {{ .ProfileID }}
   251    Name:      cities
   252    Path:      {{ .citiesPath }}
   253    Published: false
   254  1 Peername:  peer
   255    ProfileID: {{ .ProfileID }}
   256    Name:      counter
   257    Path:      {{ .counterPath }}
   258    Published: false
   259  2 Peername:  peer
   260    ProfileID: {{ .ProfileID }}
   261    Name:      craigslist
   262    Path:      {{ .craigslistPath }}
   263    Published: false
   264  3 Peername:  peer
   265    ProfileID: {{ .ProfileID }}
   266    Name:      movies
   267    Path:      {{ .moviesPath }}
   268    Published: false
   269  4 Peername:  peer
   270    ProfileID: {{ .ProfileID }}
   271    Name:      sitemap
   272    Path:      {{ .sitemapPath }}
   273    Published: false
   274  `, map[string]string{
   275  		"ProfileID":      "QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt",
   276  		"citiesPath":     "/mem/QmPWCzaxFoxAu5wS8qXkL6tSA7aR2Lpcwykfz1TbhhpuDp",
   277  		"counterPath":    "/mem/QmVN68yJdLCstVj7YiDjoDvbuxnWKL57D5EAszM7SxtXi3",
   278  		"craigslistPath": "/mem/QmTzSsKodVuQRBbcAnYhh8iHSnCA59CNsJzJxue9if9yXN",
   279  		"moviesPath":     "/mem/QmXkLt1xHqtJjjGoT2reGZLBFELsioWkJ24yDjchGpu63W",
   280  		"sitemapPath":    "/mem/QmdotsdAr5w32jToY13q4VR9CYdN9hTpkivJjwRELhGkxa",
   281  	})
   282  
   283  	if diff := cmp.Diff(expect, text); diff != "" {
   284  		t.Errorf("result mismatch (-want +got):\n%s", diff)
   285  	}
   286  }