github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/lib/integration_test.go (about) 1 package lib 2 3 import ( 4 "context" 5 "fmt" 6 "net/http/httptest" 7 "testing" 8 9 "github.com/qri-io/dataset" 10 "github.com/qri-io/ioes" 11 "github.com/qri-io/qri/auth/key" 12 "github.com/qri-io/qri/base/params" 13 "github.com/qri-io/qri/config" 14 "github.com/qri-io/qri/dsref" 15 dsrefspec "github.com/qri-io/qri/dsref/spec" 16 "github.com/qri-io/qri/registry" 17 "github.com/qri-io/qri/registry/regserver" 18 "github.com/qri-io/qri/remote" 19 repotest "github.com/qri-io/qri/repo/test" 20 ) 21 22 func TestTwoActorRegistryIntegration(t *testing.T) { 23 tr := NewNetworkIntegrationTestRunner(t, "integration_two_actor_registry") 24 defer tr.Cleanup() 25 26 nasim := tr.InitNasim(t) 27 28 // - nasim creates a dataset 29 ref := InitWorldBankDataset(tr.Ctx, t, nasim) 30 31 // - nasim publishes to the registry 32 PushToRegistry(tr.Ctx, t, nasim, ref.Alias()) 33 34 if err := AssertLogsEqual(nasim, tr.RegistryInst, ref); err != nil { 35 t.Error(err) 36 } 37 38 refs, err := tr.RegistryInst.Collection().ListRawRefs(tr.Ctx, &EmptyParams{}) 39 if err != nil { 40 t.Fatal(err) 41 } 42 t.Log(refs) 43 44 hinshun := tr.InitHinshun(t) 45 46 // - hinshun searches the registry for nasim's dataset name, gets a result 47 if results := SearchFor(tr.Ctx, t, hinshun, "bank"); len(results) < 1 { 48 t.Logf("expected at least one result in registry search") 49 } 50 51 // - hunshun fetches a preview of nasim's dataset 52 // TODO (b5) - need to use the ref returned from search results 53 t.Log(ref.String()) 54 Preview(tr.Ctx, t, hinshun, ref.String()) 55 56 // - hinshun pulls nasim's dataset 57 Pull(tr.Ctx, t, hinshun, ref.Alias()) 58 59 if err := AssertLogsEqual(nasim, hinshun, ref); err != nil { 60 t.Error(err) 61 } 62 63 // 5. nasim commits a new version 64 ref = Commit2WorldBank(tr.Ctx, t, nasim) 65 66 // 6. nasim re-publishes to the registry 67 PushToRegistry(tr.Ctx, t, nasim, ref.Alias()) 68 69 // 7. hinshun logsyncs with the registry for world bank dataset, sees multiple versions 70 _, err = hinshun.WithSource("network").Dataset().Pull(tr.Ctx, &PullParams{LogsOnly: true, Ref: ref.String()}) 71 if err != nil { 72 t.Errorf("cloning logs: %s", err) 73 } 74 75 if err := AssertLogsEqual(nasim, hinshun, ref); err != nil { 76 t.Error(err) 77 } 78 79 // TODO (b5) - assert hinshun DOES NOT have blocks for the latest commit to world bank dataset 80 81 // 8. hinshun pulls latest version 82 Pull(tr.Ctx, t, hinshun, ref.Alias()) 83 84 // TODO (b5) - assert hinshun has world bank dataset blocks 85 86 // all three should now have the same HEAD reference & InitID 87 dsrefspec.ConsistentResolvers(t, dsref.Ref{ 88 Username: ref.Username, 89 Name: ref.Name, 90 }, 91 nasim.Repo(), 92 hinshun.Repo(), 93 tr.RegistryInst.Repo(), 94 ) 95 } 96 97 func TestReferencePulling(t *testing.T) { 98 tr := NewNetworkIntegrationTestRunner(t, "integration_reference_pulling") 99 defer tr.Cleanup() 100 101 nasim := tr.InitNasim(t) 102 103 // - nasim creates a dataset, publishes to registry 104 ref := InitWorldBankDataset(tr.Ctx, t, nasim) 105 PushToRegistry(tr.Ctx, t, nasim, ref.Alias()) 106 107 // - nasim's local repo should reflect publication 108 logRes, err := nasim.Dataset().Activity(tr.Ctx, &ActivityParams{Ref: ref.Alias(), List: params.List{Limit: 1}}) 109 if err != nil { 110 t.Fatal(err) 111 } 112 113 if logRes[0].Published != true { 114 t.Errorf("nasim has published HEAD. ref[0] published is false") 115 } 116 117 hinshun := tr.InitHinshun(t) 118 119 // fetch this from the registry by default 120 p := &GetParams{Ref: "nasim/world_bank_population"} 121 if _, err := hinshun.Dataset().Get(tr.Ctx, p); err != nil { 122 t.Fatal(err) 123 } 124 125 // re-run. dataset should now be local, and no longer require registry to 126 // resolve 127 if _, err = hinshun.WithSource("local").Dataset().Get(tr.Ctx, p); err != nil { 128 t.Fatal(err) 129 } 130 131 // create adnan 132 adnan := tr.InitAdnan(t) 133 134 // run a transform script that relies on world_bank_population, which adnan's 135 // node should automatically pull to execute this script 136 tfScriptData := ` 137 wbp = load_dataset("nasim/world_bank_population") 138 ds = dataset.latest() 139 140 ds.body = wbp.body + [["g","h","i",False,3]] 141 dataset.commit(ds) 142 ` 143 scriptPath, err := tr.adnanRepo.WriteRootFile("transform.star", tfScriptData) 144 if err != nil { 145 t.Fatal(err) 146 } 147 148 saveParams := &SaveParams{ 149 Ref: "me/wbp_plus_one", 150 FilePaths: []string{ 151 scriptPath, 152 }, 153 Apply: true, 154 } 155 _, err = adnan.Dataset().Save(tr.Ctx, saveParams) 156 if err != nil { 157 t.Fatal(err) 158 } 159 160 // - adnan's local repo should reflect nasim's publication 161 logRes, err = adnan.Dataset().Activity(tr.Ctx, &ActivityParams{Ref: ref.Alias(), List: params.List{Limit: 1}}) 162 if err != nil { 163 t.Fatal(err) 164 } 165 166 if logRes[0].Published != true { 167 t.Errorf("adnan's log expects head was published, ref[0] published is false") 168 } 169 } 170 171 type NetworkIntegrationTestRunner struct { 172 Ctx context.Context 173 prefix string 174 TestCrypto key.CryptoGenerator 175 176 nasimRepo, hinshunRepo, adnanRepo *repotest.TempRepo 177 Nasim, Hinshun, Adnan *Instance 178 179 registryRepo *repotest.TempRepo 180 Registry registry.Registry 181 RegistryInst *Instance 182 RegistryHTTPServer *httptest.Server 183 } 184 185 func NewNetworkIntegrationTestRunner(t *testing.T, prefix string) *NetworkIntegrationTestRunner { 186 tr := &NetworkIntegrationTestRunner{ 187 Ctx: context.Background(), 188 prefix: prefix, 189 TestCrypto: repotest.NewTestCrypto(), 190 } 191 192 tr.InitRegistry(t) 193 194 return tr 195 } 196 197 func (tr *NetworkIntegrationTestRunner) Cleanup() { 198 if tr.RegistryHTTPServer != nil { 199 tr.RegistryHTTPServer.Close() 200 } 201 if tr.registryRepo != nil { 202 tr.registryRepo.Delete() 203 } 204 if tr.nasimRepo != nil { 205 tr.nasimRepo.Delete() 206 } 207 if tr.hinshunRepo != nil { 208 tr.hinshunRepo.Delete() 209 } 210 } 211 212 func (tr *NetworkIntegrationTestRunner) InitNasim(t *testing.T) *Instance { 213 r, err := repotest.NewTempRepo("nasim", fmt.Sprintf("%s_nasim", tr.prefix), tr.TestCrypto) 214 if err != nil { 215 t.Fatal(err) 216 } 217 218 if tr.RegistryHTTPServer != nil { 219 cfg := r.GetConfig() 220 cfg.Registry.Location = tr.RegistryHTTPServer.URL 221 r.WriteConfigFile() 222 } 223 tr.nasimRepo = &r 224 225 if tr.Nasim, err = NewInstance(tr.Ctx, r.QriPath, OptIOStreams(ioes.NewDiscardIOStreams())); err != nil { 226 t.Fatal(err) 227 } 228 229 return tr.Nasim 230 } 231 232 func (tr *NetworkIntegrationTestRunner) InitHinshun(t *testing.T) *Instance { 233 r, err := repotest.NewTempRepo("hinshun", fmt.Sprintf("%s_hinshun", tr.prefix), tr.TestCrypto) 234 if err != nil { 235 t.Fatal(err) 236 } 237 238 if tr.RegistryHTTPServer != nil { 239 cfg := r.GetConfig() 240 cfg.Registry.Location = tr.RegistryHTTPServer.URL 241 r.WriteConfigFile() 242 } 243 tr.hinshunRepo = &r 244 245 if tr.Hinshun, err = NewInstance(tr.Ctx, tr.hinshunRepo.QriPath, OptIOStreams(ioes.NewDiscardIOStreams())); err != nil { 246 t.Fatal(err) 247 } 248 249 return tr.Hinshun 250 } 251 252 func (tr *NetworkIntegrationTestRunner) InitAdnan(t *testing.T) *Instance { 253 r, err := repotest.NewTempRepo("adnan", fmt.Sprintf("%s_adnan", tr.prefix), tr.TestCrypto) 254 if err != nil { 255 t.Fatal(err) 256 } 257 258 if tr.RegistryHTTPServer != nil { 259 cfg := r.GetConfig() 260 cfg.Registry.Location = tr.RegistryHTTPServer.URL 261 r.WriteConfigFile() 262 } 263 tr.adnanRepo = &r 264 265 if tr.Adnan, err = NewInstance(tr.Ctx, r.QriPath, OptIOStreams(ioes.NewDiscardIOStreams())); err != nil { 266 t.Fatal(err) 267 } 268 269 return tr.Adnan 270 } 271 272 func (tr *NetworkIntegrationTestRunner) InitRegistry(t *testing.T) { 273 rr, err := repotest.NewTempRepo("registry", fmt.Sprintf("%s_registry", tr.prefix), tr.TestCrypto) 274 if err != nil { 275 t.Fatal(err) 276 } 277 t.Logf("registry qri path: %s", rr.QriPath) 278 279 tr.registryRepo = &rr 280 281 cfg := rr.GetConfig() 282 cfg.Registry.Location = "" 283 cfg.RemoteServer = &config.RemoteServer{ 284 Enabled: true, 285 AcceptSizeMax: -1, 286 AcceptTimeoutMs: -1, 287 RequireAllBlocks: false, 288 AllowRemoves: true, 289 } 290 291 rr.WriteConfigFile() 292 293 tr.RegistryInst, err = NewInstance(tr.Ctx, rr.QriPath, OptIOStreams(ioes.NewDiscardIOStreams())) 294 if err != nil { 295 t.Fatal(err) 296 } 297 298 node := tr.RegistryInst.Node() 299 if node == nil { 300 t.Fatal("creating a Registry for NetworkIntegration test fails if `qri connect` is running") 301 } 302 303 rem, err := remote.NewServer(node, cfg.RemoteServer, node.Repo.Logbook(), tr.RegistryInst.Bus()) 304 if err != nil { 305 t.Fatal(err) 306 } 307 308 tr.Registry = registry.Registry{ 309 Remote: rem, 310 Profiles: registry.NewMemProfiles(), 311 Search: regserver.MockRepoSearch{Repo: tr.RegistryInst.Repo()}, 312 } 313 314 _, tr.RegistryHTTPServer = regserver.NewMockServerRegistry(tr.Registry) 315 } 316 317 func AssertLogsEqual(a, b *Instance, ref dsref.Ref) error { 318 319 aLogs, err := a.logbook.DatasetRef(context.Background(), ref) 320 if err != nil { 321 return fmt.Errorf("fetching logs for a instance: %s", err) 322 } 323 324 bLogs, err := b.logbook.DatasetRef(context.Background(), ref) 325 if err != nil { 326 return fmt.Errorf("fetching logs for b instance: %s", err) 327 } 328 329 if aLogs.ID() != bLogs.ID() { 330 return fmt.Errorf("log ID mismatch. %s != %s", aLogs.ID(), bLogs.ID()) 331 } 332 333 if len(aLogs.Logs) != len(bLogs.Logs) { 334 return fmt.Errorf("oplength mismatch. %d != %d", len(aLogs.Logs), len(bLogs.Logs)) 335 } 336 337 return nil 338 } 339 340 func InitWorldBankDataset(ctx context.Context, t *testing.T, inst *Instance) dsref.Ref { 341 res, err := inst.Dataset().Save(ctx, &SaveParams{ 342 Ref: "me/world_bank_population", 343 Dataset: &dataset.Dataset{ 344 Meta: &dataset.Meta{ 345 Title: "World Bank Population", 346 }, 347 BodyPath: "body.csv", 348 BodyBytes: []byte(`a,b,c,true,2 349 d,e,f,false,3`), 350 Readme: &dataset.Readme{ 351 ScriptPath: "readme.md", 352 Text: "#World Bank Population\nhow many people live on this planet?", 353 }, 354 }, 355 }) 356 357 if err != nil { 358 log.Fatalf("saving dataset version: %s", err) 359 } 360 361 return dsref.ConvertDatasetToVersionInfo(res).SimpleRef() 362 } 363 364 func Commit2WorldBank(ctx context.Context, t *testing.T, inst *Instance) dsref.Ref { 365 res, err := inst.Dataset().Save(ctx, &SaveParams{ 366 Ref: "me/world_bank_population", 367 Dataset: &dataset.Dataset{ 368 Meta: &dataset.Meta{ 369 Title: "World Bank Population", 370 }, 371 BodyPath: "body.csv", 372 BodyBytes: []byte(`a,b,c,true,2 373 d,e,f,false,3 374 g,g,i,true,4`), 375 }, 376 }) 377 378 if err != nil { 379 log.Fatalf("saving dataset version: %s", err) 380 } 381 382 return dsref.ConvertDatasetToVersionInfo(res).SimpleRef() 383 } 384 385 func PushToRegistry(ctx context.Context, t *testing.T, inst *Instance, refstr string) dsref.Ref { 386 res, err := inst.WithSource("local").Dataset().Push(ctx, &PushParams{ 387 Ref: refstr, 388 }) 389 390 if err != nil { 391 t.Fatalf("publishing dataset: %s", err) 392 } 393 394 return *res 395 } 396 397 func SearchFor(ctx context.Context, t *testing.T, inst *Instance, term string) []registry.SearchResult { 398 results, err := inst.Search().Search(ctx, &SearchParams{Query: term}) 399 if err != nil { 400 t.Fatal(err) 401 } 402 403 return results 404 } 405 406 func Pull(ctx context.Context, t *testing.T, inst *Instance, refstr string) *dataset.Dataset { 407 t.Helper() 408 res, err := inst.WithSource("network").Dataset().Pull(ctx, &PullParams{Ref: refstr}) 409 if err != nil { 410 t.Fatalf("cloning dataset %s: %s", refstr, err) 411 } 412 return res 413 } 414 415 func Preview(ctx context.Context, t *testing.T, inst *Instance, ref string) *dataset.Dataset { 416 t.Helper() 417 p := &PreviewParams{ 418 Ref: ref, 419 } 420 res, err := inst.Remote().Preview(ctx, p) 421 if err != nil { 422 t.Fatal(err) 423 } 424 return res 425 }