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 }