github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/index/indextest/tests.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package indextest contains the unit tests for the indexer so they 18 // can be re-used for each specific implementation of the index 19 // Storage interface. 20 package indextest 21 22 import ( 23 "bytes" 24 "fmt" 25 "io/ioutil" 26 "log" 27 "net/url" 28 "os" 29 "path/filepath" 30 "reflect" 31 "testing" 32 "time" 33 34 "camlistore.org/pkg/blob" 35 "camlistore.org/pkg/index" 36 "camlistore.org/pkg/jsonsign" 37 "camlistore.org/pkg/osutil" 38 "camlistore.org/pkg/schema" 39 "camlistore.org/pkg/test" 40 "camlistore.org/pkg/types/camtypes" 41 ) 42 43 // An IndexDeps is a helper for populating and querying an Index for tests. 44 type IndexDeps struct { 45 Index *index.Index 46 47 BlobSource *test.Fetcher 48 49 // Following three needed for signing: 50 PublicKeyFetcher *test.Fetcher 51 EntityFetcher jsonsign.EntityFetcher // fetching decrypted openpgp entities 52 SignerBlobRef blob.Ref 53 54 now time.Time // fake clock, nanos since epoch 55 56 Fataler // optional means of failing. 57 } 58 59 type Fataler interface { 60 Fatalf(format string, args ...interface{}) 61 } 62 63 type logFataler struct{} 64 65 func (logFataler) Fatalf(format string, args ...interface{}) { 66 log.Fatalf(format, args...) 67 } 68 69 func (id *IndexDeps) Get(key string) string { 70 v, _ := id.Index.Storage().Get(key) 71 return v 72 } 73 74 func (id *IndexDeps) Set(key, value string) error { 75 return id.Index.Storage().Set(key, value) 76 } 77 78 func (id *IndexDeps) DumpIndex(t *testing.T) { 79 t.Logf("Begin index dump:") 80 it := id.Index.Storage().Find("", "") 81 for it.Next() { 82 t.Logf(" %q = %q", it.Key(), it.Value()) 83 } 84 if err := it.Close(); err != nil { 85 t.Fatalf("iterator close = %v", err) 86 } 87 t.Logf("End index dump.") 88 } 89 90 func (id *IndexDeps) uploadAndSign(m *schema.Builder) blob.Ref { 91 m.SetSigner(id.SignerBlobRef) 92 unsigned, err := m.JSON() 93 if err != nil { 94 id.Fatalf("uploadAndSignMap: " + err.Error()) 95 } 96 sr := &jsonsign.SignRequest{ 97 UnsignedJSON: unsigned, 98 Fetcher: id.PublicKeyFetcher, 99 EntityFetcher: id.EntityFetcher, 100 SignatureTime: id.now, 101 } 102 signed, err := sr.Sign() 103 if err != nil { 104 id.Fatalf("problem signing: " + err.Error()) 105 } 106 tb := &test.Blob{Contents: signed} 107 _, err = id.Index.ReceiveBlob(tb.BlobRef(), tb.Reader()) 108 if err != nil { 109 id.Fatalf("problem indexing blob: %v\nblob was:\n%s", err, signed) 110 } 111 return tb.BlobRef() 112 } 113 114 // NewPermanode creates (& signs) a new permanode and adds it 115 // to the index, returning its blobref. 116 func (id *IndexDeps) NewPermanode() blob.Ref { 117 unsigned := schema.NewUnsignedPermanode() 118 return id.uploadAndSign(unsigned) 119 } 120 121 // NewPermanode creates (& signs) a new planned permanode and adds it 122 // to the index, returning its blobref. 123 func (id *IndexDeps) NewPlannedPermanode(key string) blob.Ref { 124 unsigned := schema.NewPlannedPermanode(key) 125 return id.uploadAndSign(unsigned) 126 } 127 128 func (id *IndexDeps) advanceTime() time.Time { 129 id.now = id.now.Add(1 * time.Second) 130 return id.now 131 } 132 133 func (id *IndexDeps) lastTime() time.Time { 134 return id.now 135 } 136 137 func (id *IndexDeps) SetAttribute(permaNode blob.Ref, attr, value string) blob.Ref { 138 m := schema.NewSetAttributeClaim(permaNode, attr, value) 139 m.SetClaimDate(id.advanceTime()) 140 return id.uploadAndSign(m) 141 } 142 143 func (id *IndexDeps) SetAttribute_NoTimeMove(permaNode blob.Ref, attr, value string) blob.Ref { 144 m := schema.NewSetAttributeClaim(permaNode, attr, value) 145 m.SetClaimDate(id.lastTime()) 146 return id.uploadAndSign(m) 147 } 148 149 func (id *IndexDeps) AddAttribute(permaNode blob.Ref, attr, value string) blob.Ref { 150 m := schema.NewAddAttributeClaim(permaNode, attr, value) 151 m.SetClaimDate(id.advanceTime()) 152 return id.uploadAndSign(m) 153 } 154 155 func (id *IndexDeps) DelAttribute(permaNode blob.Ref, attr, value string) blob.Ref { 156 m := schema.NewDelAttributeClaim(permaNode, attr, value) 157 m.SetClaimDate(id.advanceTime()) 158 return id.uploadAndSign(m) 159 } 160 161 func (id *IndexDeps) Delete(target blob.Ref) blob.Ref { 162 m := schema.NewDeleteClaim(target) 163 m.SetClaimDate(id.advanceTime()) 164 return id.uploadAndSign(m) 165 } 166 167 var noTime = time.Time{} 168 169 func (id *IndexDeps) UploadString(v string) blob.Ref { 170 cb := &test.Blob{Contents: v} 171 id.BlobSource.AddBlob(cb) 172 br := cb.BlobRef() 173 _, err := id.Index.ReceiveBlob(br, cb.Reader()) 174 if err != nil { 175 id.Fatalf("UploadString: %v", err) 176 } 177 return br 178 } 179 180 // If modTime is zero, it's not used. 181 func (id *IndexDeps) UploadFile(fileName string, contents string, modTime time.Time) (fileRef, wholeRef blob.Ref) { 182 wholeRef = id.UploadString(contents) 183 184 m := schema.NewFileMap(fileName) 185 m.PopulateParts(int64(len(contents)), []schema.BytesPart{ 186 schema.BytesPart{ 187 Size: uint64(len(contents)), 188 BlobRef: wholeRef, 189 }}) 190 if !modTime.IsZero() { 191 m.SetModTime(modTime) 192 } 193 fjson, err := m.JSON() 194 if err != nil { 195 id.Fatalf("UploadFile.JSON: %v", err) 196 } 197 fb := &test.Blob{Contents: fjson} 198 id.BlobSource.AddBlob(fb) 199 fileRef = fb.BlobRef() 200 _, err = id.Index.ReceiveBlob(fileRef, fb.Reader()) 201 if err != nil { 202 panic(err) 203 } 204 return 205 } 206 207 // If modTime is zero, it's not used. 208 func (id *IndexDeps) UploadDir(dirName string, children []blob.Ref, modTime time.Time) blob.Ref { 209 // static-set entries blob 210 ss := new(schema.StaticSet) 211 for _, child := range children { 212 ss.Add(child) 213 } 214 ssjson := ss.Blob().JSON() 215 ssb := &test.Blob{Contents: ssjson} 216 id.BlobSource.AddBlob(ssb) 217 _, err := id.Index.ReceiveBlob(ssb.BlobRef(), ssb.Reader()) 218 if err != nil { 219 id.Fatalf("UploadDir.ReceiveBlob: %v", err) 220 } 221 222 // directory blob 223 bb := schema.NewDirMap(dirName) 224 bb.PopulateDirectoryMap(ssb.BlobRef()) 225 if !modTime.IsZero() { 226 bb.SetModTime(modTime) 227 } 228 dirjson, err := bb.JSON() 229 if err != nil { 230 id.Fatalf("UploadDir.JSON: %v", err) 231 } 232 dirb := &test.Blob{Contents: dirjson} 233 id.BlobSource.AddBlob(dirb) 234 _, err = id.Index.ReceiveBlob(dirb.BlobRef(), dirb.Reader()) 235 if err != nil { 236 id.Fatalf("UploadDir.ReceiveBlob: %v", err) 237 } 238 return dirb.BlobRef() 239 } 240 241 // NewIndexDeps returns an IndexDeps helper for populating and working 242 // with the provided index for tests. 243 func NewIndexDeps(index *index.Index) *IndexDeps { 244 camliRootPath, err := osutil.GoPackagePath("camlistore.org") 245 if err != nil { 246 log.Fatal("Package camlistore.org no found in $GOPATH or $GOPATH not defined") 247 } 248 secretRingFile := filepath.Join(camliRootPath, "pkg", "jsonsign", "testdata", "test-secring.gpg") 249 pubKey := &test.Blob{Contents: `-----BEGIN PGP PUBLIC KEY BLOCK----- 250 251 xsBNBEzgoVsBCAC/56aEJ9BNIGV9FVP+WzenTAkg12k86YqlwJVAB/VwdMlyXxvi 252 bCT1RVRfnYxscs14LLfcMWF3zMucw16mLlJCBSLvbZ0jn4h+/8vK5WuAdjw2YzLs 253 WtBcjWn3lV6tb4RJz5gtD/o1w8VWxwAnAVIWZntKAWmkcChCRgdUeWso76+plxE5 254 aRYBJqdT1mctGqNEISd/WYPMgwnWXQsVi3x4z1dYu2tD9uO1dkAff12z1kyZQIBQ 255 rexKYRRRh9IKAayD4kgS0wdlULjBU98aeEaMz1ckuB46DX3lAYqmmTEL/Rl9cOI0 256 Enpn/oOOfYFa5h0AFndZd1blMvruXfdAobjVABEBAAE= 257 =28/7 258 -----END PGP PUBLIC KEY BLOCK-----`} 259 260 id := &IndexDeps{ 261 Index: index, 262 BlobSource: new(test.Fetcher), 263 PublicKeyFetcher: new(test.Fetcher), 264 EntityFetcher: &jsonsign.CachingEntityFetcher{ 265 Fetcher: &jsonsign.FileEntityFetcher{File: secretRingFile}, 266 }, 267 SignerBlobRef: pubKey.BlobRef(), 268 now: test.ClockOrigin, 269 Fataler: logFataler{}, 270 } 271 // Add dev client test key public key, keyid 26F5ABDA, 272 // blobref sha1-ad87ca5c78bd0ce1195c46f7c98e6025abbaf007 273 if g, w := id.SignerBlobRef.String(), "sha1-ad87ca5c78bd0ce1195c46f7c98e6025abbaf007"; g != w { 274 id.Fatalf("unexpected signer blobref; got signer = %q; want %q", g, w) 275 } 276 id.PublicKeyFetcher.AddBlob(pubKey) 277 id.Index.KeyFetcher = id.PublicKeyFetcher 278 id.Index.BlobSource = id.BlobSource 279 return id 280 } 281 282 func Index(t *testing.T, initIdx func() *index.Index) { 283 id := NewIndexDeps(initIdx()) 284 id.Fataler = t 285 defer id.DumpIndex(t) 286 pn := id.NewPermanode() 287 t.Logf("uploaded permanode %q", pn) 288 br1 := id.SetAttribute(pn, "tag", "foo1") 289 br1Time := id.lastTime() 290 t.Logf("set attribute %q", br1) 291 br2 := id.SetAttribute(pn, "tag", "foo2") 292 br2Time := id.lastTime() 293 t.Logf("set attribute %q", br2) 294 rootClaim := id.SetAttribute(pn, "camliRoot", "rootval") 295 rootClaimTime := id.lastTime() 296 t.Logf("set attribute %q", rootClaim) 297 298 pnChild := id.NewPermanode() 299 br3 := id.SetAttribute(pnChild, "tag", "bar") 300 br3Time := id.lastTime() 301 t.Logf("set attribute %q", br3) 302 memberRef := id.AddAttribute(pn, "camliMember", pnChild.String()) 303 t.Logf("add-attribute claim %q points to member permanode %q", memberRef, pnChild) 304 memberRefTime := id.lastTime() 305 306 // TODO(bradfitz): add EXIF tests here, once that stuff is ready. 307 if false { 308 camliRootPath, err := osutil.GoPackagePath("camlistore.org") 309 if err != nil { 310 t.Fatal("Package camlistore.org no found in $GOPATH or $GOPATH not defined") 311 } 312 for i := 1; i <= 8; i++ { 313 fileBase := fmt.Sprintf("f%d-exif.jpg", i) 314 fileName := filepath.Join(camliRootPath, "pkg", "images", "testdata", fileBase) 315 contents, err := ioutil.ReadFile(fileName) 316 if err != nil { 317 t.Fatal(err) 318 } 319 id.UploadFile(fileBase, string(contents), noTime) 320 } 321 } 322 323 // Upload some files. 324 var jpegFileRef, exifFileRef, mediaFileRef, mediaWholeRef blob.Ref 325 { 326 camliRootPath, err := osutil.GoPackagePath("camlistore.org") 327 if err != nil { 328 t.Fatal("Package camlistore.org no found in $GOPATH or $GOPATH not defined") 329 } 330 uploadFile := func(file string, modTime time.Time) (fileRef, wholeRef blob.Ref) { 331 fileName := filepath.Join(camliRootPath, "pkg", "index", "indextest", "testdata", file) 332 contents, err := ioutil.ReadFile(fileName) 333 if err != nil { 334 t.Fatal(err) 335 } 336 fileRef, wholeRef = id.UploadFile(file, string(contents), modTime) 337 return 338 } 339 jpegFileRef, _ = uploadFile("dude.jpg", noTime) 340 exifFileRef, _ = uploadFile("dude-exif.jpg", time.Unix(1361248796, 0)) 341 mediaFileRef, mediaWholeRef = uploadFile("0s.mp3", noTime) 342 } 343 344 // Upload the dir containing the previous files. 345 imagesDirRef := id.UploadDir( 346 "testdata", 347 []blob.Ref{jpegFileRef, exifFileRef, mediaFileRef}, 348 time.Now(), 349 ) 350 351 lastPermanodeMutation := id.lastTime() 352 353 key := "signerkeyid:sha1-ad87ca5c78bd0ce1195c46f7c98e6025abbaf007" 354 if g, e := id.Get(key), "2931A67C26F5ABDA"; g != e { 355 t.Fatalf("%q = %q, want %q", key, g, e) 356 } 357 358 key = "imagesize|" + jpegFileRef.String() 359 if g, e := id.Get(key), "50|100"; g != e { 360 t.Errorf("JPEG dude.jpg key %q = %q; want %q", key, g, e) 361 } 362 key = "filetimes|" + jpegFileRef.String() 363 if g, e := id.Get(key), ""; g != e { 364 t.Errorf("JPEG dude.jpg key %q = %q; want %q", key, g, e) 365 } 366 367 key = "filetimes|" + exifFileRef.String() 368 if g, e := id.Get(key), "2013-02-18T01%3A11%3A20Z%2C2013-02-19T04%3A39%3A56Z"; g != e { 369 t.Errorf("EXIF dude-exif.jpg key %q = %q; want %q", key, g, e) 370 } 371 372 key = "have:" + pn.String() 373 pnSizeStr := id.Get(key) 374 if pnSizeStr == "" { 375 t.Fatalf("missing key %q", key) 376 } 377 378 key = "meta:" + pn.String() 379 if g, e := id.Get(key), pnSizeStr+"|application/json; camliType=permanode"; g != e { 380 t.Errorf("key %q = %q, want %q", key, g, e) 381 } 382 383 key = "recpn|2931A67C26F5ABDA|rt7988-88-71T98:67:62.999876543Z|" + br1.String() 384 if g, e := id.Get(key), pn.String(); g != e { 385 t.Fatalf("%q = %q, want %q (permanode)", key, g, e) 386 } 387 388 key = "recpn|2931A67C26F5ABDA|rt7988-88-71T98:67:61.999876543Z|" + br2.String() 389 if g, e := id.Get(key), pn.String(); g != e { 390 t.Fatalf("%q = %q, want %q (permanode)", key, g, e) 391 } 392 393 key = fmt.Sprintf("edgeback|%s|%s|%s", pnChild, pn, memberRef) 394 if g, e := id.Get(key), "permanode|"; g != e { 395 t.Fatalf("edgeback row %q = %q, want %q", key, g, e) 396 } 397 398 mediaTests := []struct { 399 prop, exp string 400 }{ 401 {"title", "Zero Seconds"}, 402 {"artist", "Test Artist"}, 403 {"album", "Test Album"}, 404 {"genre", "(20)Alternative"}, 405 {"year", "1992"}, 406 {"track", "1"}, 407 } 408 for _, tt := range mediaTests { 409 key = fmt.Sprintf("mediatag|%s|%s", mediaWholeRef.String(), tt.prop) 410 if g, _ := url.QueryUnescape(id.Get(key)); g != tt.exp { 411 t.Errorf("0s.mp3 key %q = %q; want %q", key, g, tt.exp) 412 } 413 } 414 415 // PermanodeOfSignerAttrValue 416 { 417 gotPN, err := id.Index.PermanodeOfSignerAttrValue(id.SignerBlobRef, "camliRoot", "rootval") 418 if err != nil { 419 t.Fatalf("id.Index.PermanodeOfSignerAttrValue = %v", err) 420 } 421 if gotPN.String() != pn.String() { 422 t.Errorf("id.Index.PermanodeOfSignerAttrValue = %q, want %q", gotPN, pn) 423 } 424 _, err = id.Index.PermanodeOfSignerAttrValue(id.SignerBlobRef, "camliRoot", "MISSING") 425 if err == nil { 426 t.Errorf("expected an error from PermanodeOfSignerAttrValue on missing value") 427 } 428 } 429 430 // SearchPermanodesWithAttr - match attr type "tag" and value "foo1" 431 { 432 ch := make(chan blob.Ref, 10) 433 req := &camtypes.PermanodeByAttrRequest{ 434 Signer: id.SignerBlobRef, 435 Attribute: "tag", 436 Query: "foo1", 437 } 438 err := id.Index.SearchPermanodesWithAttr(ch, req) 439 if err != nil { 440 t.Fatalf("SearchPermanodesWithAttr = %v", err) 441 } 442 var got []blob.Ref 443 for r := range ch { 444 got = append(got, r) 445 } 446 want := []blob.Ref{pn} 447 if len(got) < 1 || got[0].String() != want[0].String() { 448 t.Errorf("id.Index.SearchPermanodesWithAttr gives %q, want %q", got, want) 449 } 450 } 451 452 // SearchPermanodesWithAttr - match all with attr type "tag" 453 { 454 ch := make(chan blob.Ref, 10) 455 req := &camtypes.PermanodeByAttrRequest{ 456 Signer: id.SignerBlobRef, 457 Attribute: "tag", 458 } 459 err := id.Index.SearchPermanodesWithAttr(ch, req) 460 if err != nil { 461 t.Fatalf("SearchPermanodesWithAttr = %v", err) 462 } 463 var got []blob.Ref 464 for r := range ch { 465 got = append(got, r) 466 } 467 want := []blob.Ref{pn, pnChild} 468 if len(got) != len(want) { 469 t.Errorf("SearchPermanodesWithAttr results differ.\n got: %q\nwant: %q", 470 got, want) 471 } 472 for _, w := range want { 473 found := false 474 for _, g := range got { 475 if g.String() == w.String() { 476 found = true 477 break 478 } 479 } 480 if !found { 481 t.Errorf("SearchPermanodesWithAttr: %v was not found.\n", w) 482 } 483 } 484 } 485 486 // Delete value "pony" of type "title" (which does not actually exist) for pn 487 br4 := id.DelAttribute(pn, "title", "pony") 488 br4Time := id.lastTime() 489 // and verify it is not found when searching by attr 490 { 491 ch := make(chan blob.Ref, 10) 492 req := &camtypes.PermanodeByAttrRequest{ 493 Signer: id.SignerBlobRef, 494 Attribute: "title", 495 Query: "pony", 496 } 497 err := id.Index.SearchPermanodesWithAttr(ch, req) 498 if err != nil { 499 t.Fatalf("SearchPermanodesWithAttr = %v", err) 500 } 501 var got []blob.Ref 502 for r := range ch { 503 got = append(got, r) 504 } 505 want := []blob.Ref{} 506 if len(got) != len(want) { 507 t.Errorf("SearchPermanodesWithAttr results differ.\n got: %q\nwant: %q", 508 got, want) 509 } 510 } 511 512 // GetRecentPermanodes 513 { 514 verify := func(prefix string, want []camtypes.RecentPermanode, before time.Time) { 515 ch := make(chan camtypes.RecentPermanode, 10) // expect 2 results, but maybe more if buggy. 516 err := id.Index.GetRecentPermanodes(ch, id.SignerBlobRef, 50, before) 517 if err != nil { 518 t.Fatalf("[%s] GetRecentPermanodes = %v", prefix, err) 519 } 520 got := []camtypes.RecentPermanode{} 521 for r := range ch { 522 got = append(got, r) 523 } 524 if len(got) != len(want) { 525 t.Errorf("[%s] GetRecentPermanode results differ.\n got: %v\nwant: %v", 526 prefix, searchResults(got), searchResults(want)) 527 } 528 for _, w := range want { 529 found := false 530 for _, g := range got { 531 if g.Equal(w) { 532 found = true 533 break 534 } 535 } 536 if !found { 537 t.Errorf("[%s] GetRecentPermanode: %v was not found.\n got: %v\nwant: %v", 538 prefix, w, searchResults(got), searchResults(want)) 539 } 540 } 541 } 542 543 want := []camtypes.RecentPermanode{ 544 { 545 Permanode: pn, 546 Signer: id.SignerBlobRef, 547 LastModTime: br4Time, 548 }, 549 { 550 Permanode: pnChild, 551 Signer: id.SignerBlobRef, 552 LastModTime: br3Time, 553 }, 554 } 555 556 before := time.Time{} 557 verify("Zero before", want, before) 558 559 before = lastPermanodeMutation 560 t.Log("lastPermanodeMutation", lastPermanodeMutation, 561 lastPermanodeMutation.Unix()) 562 verify("Non-zero before", want[1:], before) 563 } 564 // GetDirMembers 565 { 566 ch := make(chan blob.Ref, 10) // expect 2 results 567 err := id.Index.GetDirMembers(imagesDirRef, ch, 50) 568 if err != nil { 569 t.Fatalf("GetDirMembers = %v", err) 570 } 571 got := []blob.Ref{} 572 for r := range ch { 573 got = append(got, r) 574 } 575 want := []blob.Ref{jpegFileRef, exifFileRef, mediaFileRef} 576 if len(got) != len(want) { 577 t.Errorf("GetDirMembers results differ.\n got: %v\nwant: %v", 578 got, want) 579 } 580 for _, w := range want { 581 found := false 582 for _, g := range got { 583 if w == g { 584 found = true 585 break 586 } 587 } 588 if !found { 589 t.Errorf("GetDirMembers: %v was not found.", w) 590 } 591 } 592 } 593 594 // GetBlobMeta 595 { 596 meta, err := id.Index.GetBlobMeta(pn) 597 if err != nil { 598 t.Errorf("GetBlobMeta(%q) = %v", pn, err) 599 } else { 600 if e := "permanode"; meta.CamliType != e { 601 t.Errorf("GetBlobMeta(%q) mime = %q, want %q", pn, meta.CamliType, e) 602 } 603 if meta.Size == 0 { 604 t.Errorf("GetBlobMeta(%q) size is zero", pn) 605 } 606 } 607 _, err = id.Index.GetBlobMeta(blob.ParseOrZero("abc-123")) 608 if err != os.ErrNotExist { 609 t.Errorf("GetBlobMeta(dummy blobref) = %v; want os.ErrNotExist", err) 610 } 611 } 612 613 // AppendClaims 614 { 615 claims, err := id.Index.AppendClaims(nil, pn, id.SignerBlobRef, "") 616 if err != nil { 617 t.Errorf("AppendClaims = %v", err) 618 } else { 619 want := []camtypes.Claim{ 620 { 621 BlobRef: br1, 622 Permanode: pn, 623 Signer: id.SignerBlobRef, 624 Date: br1Time.UTC(), 625 Type: "set-attribute", 626 Attr: "tag", 627 Value: "foo1", 628 }, 629 { 630 BlobRef: br2, 631 Permanode: pn, 632 Signer: id.SignerBlobRef, 633 Date: br2Time.UTC(), 634 Type: "set-attribute", 635 Attr: "tag", 636 Value: "foo2", 637 }, 638 { 639 BlobRef: rootClaim, 640 Permanode: pn, 641 Signer: id.SignerBlobRef, 642 Date: rootClaimTime.UTC(), 643 Type: "set-attribute", 644 Attr: "camliRoot", 645 Value: "rootval", 646 }, 647 { 648 BlobRef: memberRef, 649 Permanode: pn, 650 Signer: id.SignerBlobRef, 651 Date: memberRefTime.UTC(), 652 Type: "add-attribute", 653 Attr: "camliMember", 654 Value: pnChild.String(), 655 }, 656 { 657 BlobRef: br4, 658 Permanode: pn, 659 Signer: id.SignerBlobRef, 660 Date: br4Time.UTC(), 661 Type: "del-attribute", 662 Attr: "title", 663 Value: "pony", 664 }, 665 } 666 if !reflect.DeepEqual(claims, want) { 667 t.Errorf("AppendClaims results differ.\n got: %v\nwant: %v", 668 claims, want) 669 } 670 } 671 } 672 } 673 674 func PathsOfSignerTarget(t *testing.T, initIdx func() *index.Index) { 675 id := NewIndexDeps(initIdx()) 676 id.Fataler = t 677 defer id.DumpIndex(t) 678 signer := id.SignerBlobRef 679 pn := id.NewPermanode() 680 t.Logf("uploaded permanode %q", pn) 681 682 claim1 := id.SetAttribute(pn, "camliPath:somedir", "targ-123") 683 claim1Time := id.lastTime().UTC() 684 claim2 := id.SetAttribute(pn, "camliPath:with|pipe", "targ-124") 685 claim2Time := id.lastTime().UTC() 686 t.Logf("made path claims %q and %q", claim1, claim2) 687 688 type test struct { 689 blobref string 690 want int 691 } 692 tests := []test{ 693 {"targ-123", 1}, 694 {"targ-124", 1}, 695 {"targ-125", 0}, 696 } 697 for _, tt := range tests { 698 paths, err := id.Index.PathsOfSignerTarget(signer, blob.ParseOrZero(tt.blobref)) 699 if err != nil { 700 t.Fatalf("PathsOfSignerTarget(%q): %v", tt.blobref, err) 701 } 702 if len(paths) != tt.want { 703 t.Fatalf("PathsOfSignerTarget(%q) got %d results; want %d", 704 tt.blobref, len(paths), tt.want) 705 } 706 if tt.blobref == "targ-123" { 707 p := paths[0] 708 want := fmt.Sprintf( 709 "Path{Claim: %s, %v; Base: %s + Suffix \"somedir\" => Target targ-123}", 710 claim1, claim1Time, pn) 711 if g := p.String(); g != want { 712 t.Errorf("claim wrong.\n got: %s\nwant: %s", g, want) 713 } 714 } 715 } 716 tests = []test{ 717 {"somedir", 1}, 718 {"with|pipe", 1}, 719 {"void", 0}, 720 } 721 for _, tt := range tests { 722 paths, err := id.Index.PathsLookup(id.SignerBlobRef, pn, tt.blobref) 723 if err != nil { 724 t.Fatalf("PathsLookup(%q): %v", tt.blobref, err) 725 } 726 if len(paths) != tt.want { 727 t.Fatalf("PathsLookup(%q) got %d results; want %d", 728 tt.blobref, len(paths), tt.want) 729 } 730 if tt.blobref == "with|pipe" { 731 p := paths[0] 732 want := fmt.Sprintf( 733 "Path{Claim: %s, %s; Base: %s + Suffix \"with|pipe\" => Target targ-124}", 734 claim2, claim2Time, pn) 735 if g := p.String(); g != want { 736 t.Errorf("claim wrong.\n got: %s\nwant: %s", g, want) 737 } 738 } 739 } 740 741 // now test deletions 742 // Delete an existing value 743 claim3 := id.Delete(claim2) 744 t.Logf("claim %q deletes path claim %q", claim3, claim2) 745 tests = []test{ 746 {"targ-123", 1}, 747 {"targ-124", 0}, 748 {"targ-125", 0}, 749 } 750 for _, tt := range tests { 751 signer := id.SignerBlobRef 752 paths, err := id.Index.PathsOfSignerTarget(signer, blob.ParseOrZero(tt.blobref)) 753 if err != nil { 754 t.Fatalf("PathsOfSignerTarget(%q): %v", tt.blobref, err) 755 } 756 if len(paths) != tt.want { 757 t.Fatalf("PathsOfSignerTarget(%q) got %d results; want %d", 758 tt.blobref, len(paths), tt.want) 759 } 760 } 761 tests = []test{ 762 {"somedir", 1}, 763 {"with|pipe", 0}, 764 {"void", 0}, 765 } 766 for _, tt := range tests { 767 paths, err := id.Index.PathsLookup(id.SignerBlobRef, pn, tt.blobref) 768 if err != nil { 769 t.Fatalf("PathsLookup(%q): %v", tt.blobref, err) 770 } 771 if len(paths) != tt.want { 772 t.Fatalf("PathsLookup(%q) got %d results; want %d", 773 tt.blobref, len(paths), tt.want) 774 } 775 } 776 777 // recreate second path, and test if the previous deletion of it 778 // is indeed ignored. 779 claim4 := id.Delete(claim3) 780 t.Logf("delete claim %q deletes claim %q, which should undelete %q", claim4, claim3, claim2) 781 tests = []test{ 782 {"targ-123", 1}, 783 {"targ-124", 1}, 784 {"targ-125", 0}, 785 } 786 for _, tt := range tests { 787 signer := id.SignerBlobRef 788 paths, err := id.Index.PathsOfSignerTarget(signer, blob.ParseOrZero(tt.blobref)) 789 if err != nil { 790 t.Fatalf("PathsOfSignerTarget(%q): %v", tt.blobref, err) 791 } 792 if len(paths) != tt.want { 793 t.Fatalf("PathsOfSignerTarget(%q) got %d results; want %d", 794 tt.blobref, len(paths), tt.want) 795 } 796 // and check the modtime too 797 if tt.blobref == "targ-124" { 798 p := paths[0] 799 want := fmt.Sprintf( 800 "Path{Claim: %s, %v; Base: %s + Suffix \"with|pipe\" => Target targ-124}", 801 claim2, claim2Time, pn) 802 if g := p.String(); g != want { 803 t.Errorf("claim wrong.\n got: %s\nwant: %s", g, want) 804 } 805 } 806 } 807 tests = []test{ 808 {"somedir", 1}, 809 {"with|pipe", 1}, 810 {"void", 0}, 811 } 812 for _, tt := range tests { 813 paths, err := id.Index.PathsLookup(id.SignerBlobRef, pn, tt.blobref) 814 if err != nil { 815 t.Fatalf("PathsLookup(%q): %v", tt.blobref, err) 816 } 817 if len(paths) != tt.want { 818 t.Fatalf("PathsLookup(%q) got %d results; want %d", 819 tt.blobref, len(paths), tt.want) 820 } 821 // and check that modtime is now claim4Time 822 if tt.blobref == "with|pipe" { 823 p := paths[0] 824 want := fmt.Sprintf( 825 "Path{Claim: %s, %s; Base: %s + Suffix \"with|pipe\" => Target targ-124}", 826 claim2, claim2Time, pn) 827 if g := p.String(); g != want { 828 t.Errorf("claim wrong.\n got: %s\nwant: %s", g, want) 829 } 830 } 831 } 832 } 833 834 func Files(t *testing.T, initIdx func() *index.Index) { 835 id := NewIndexDeps(initIdx()) 836 id.Fataler = t 837 fileTime := time.Unix(1361250375, 0) 838 fileRef, wholeRef := id.UploadFile("foo.html", "<html>I am an html file.</html>", fileTime) 839 t.Logf("uploaded fileref %q, wholeRef %q", fileRef, wholeRef) 840 id.DumpIndex(t) 841 842 // ExistingFileSchemas 843 { 844 key := fmt.Sprintf("wholetofile|%s|%s", wholeRef, fileRef) 845 if g, e := id.Get(key), "1"; g != e { 846 t.Fatalf("%q = %q, want %q", key, g, e) 847 } 848 849 refs, err := id.Index.ExistingFileSchemas(wholeRef) 850 if err != nil { 851 t.Fatalf("ExistingFileSchemas = %v", err) 852 } 853 want := []blob.Ref{fileRef} 854 if !reflect.DeepEqual(refs, want) { 855 t.Errorf("ExistingFileSchemas got = %#v, want %#v", refs, want) 856 } 857 } 858 859 // FileInfo 860 { 861 key := fmt.Sprintf("fileinfo|%s", fileRef) 862 if g, e := id.Get(key), "31|foo.html|text%2Fhtml"; g != e { 863 t.Fatalf("%q = %q, want %q", key, g, e) 864 } 865 866 fi, err := id.Index.GetFileInfo(fileRef) 867 if err != nil { 868 t.Fatalf("GetFileInfo = %v", err) 869 } 870 if g, e := fi.Size, int64(31); g != e { 871 t.Errorf("Size = %d, want %d", g, e) 872 } 873 if g, e := fi.FileName, "foo.html"; g != e { 874 t.Errorf("FileName = %q, want %q", g, e) 875 } 876 if g, e := fi.MIMEType, "text/html"; g != e { 877 t.Errorf("MIMEType = %q, want %q", g, e) 878 } 879 if g, e := fi.Time, fileTime; !g.Time().Equal(e) { 880 t.Errorf("Time = %v; want %v", g, e) 881 } 882 } 883 } 884 885 func EdgesTo(t *testing.T, initIdx func() *index.Index) { 886 idx := initIdx() 887 id := NewIndexDeps(idx) 888 id.Fataler = t 889 defer id.DumpIndex(t) 890 891 // pn1 ---member---> pn2 892 pn1 := id.NewPermanode() 893 pn2 := id.NewPermanode() 894 claim1 := id.AddAttribute(pn1, "camliMember", pn2.String()) 895 896 t.Logf("edge %s --> %s", pn1, pn2) 897 898 // Look for pn1 899 { 900 edges, err := idx.EdgesTo(pn2, nil) 901 if err != nil { 902 t.Fatal(err) 903 } 904 if len(edges) != 1 { 905 t.Fatalf("num edges = %d; want 1", len(edges)) 906 } 907 wantEdge := &camtypes.Edge{ 908 From: pn1, 909 To: pn2, 910 FromType: "permanode", 911 } 912 if got, want := edges[0].String(), wantEdge.String(); got != want { 913 t.Errorf("Wrong edge.\n GOT: %v\nWANT: %v", got, want) 914 } 915 } 916 917 // Delete claim -> break edge relationship. 918 del1 := id.Delete(claim1) 919 t.Logf("del claim %q deletes claim %q, breaks link between p1 and p2", del1, claim1) 920 // test that we can't find anymore pn1 from pn2 921 { 922 edges, err := idx.EdgesTo(pn2, nil) 923 if err != nil { 924 t.Fatal(err) 925 } 926 if len(edges) != 0 { 927 t.Fatalf("num edges = %d; want 0", len(edges)) 928 } 929 } 930 931 // Undelete, should restore the link. 932 del2 := id.Delete(del1) 933 t.Logf("del claim %q deletes del claim %q, restores link between p1 and p2", del2, del1) 934 { 935 edges, err := idx.EdgesTo(pn2, nil) 936 if err != nil { 937 t.Fatal(err) 938 } 939 if len(edges) != 1 { 940 t.Fatalf("num edges = %d; want 1", len(edges)) 941 } 942 wantEdge := &camtypes.Edge{ 943 From: pn1, 944 To: pn2, 945 FromType: "permanode", 946 } 947 if got, want := edges[0].String(), wantEdge.String(); got != want { 948 t.Errorf("Wrong edge.\n GOT: %v\nWANT: %v", got, want) 949 } 950 } 951 } 952 953 func Delete(t *testing.T, initIdx func() *index.Index) { 954 idx := initIdx() 955 id := NewIndexDeps(idx) 956 id.Fataler = t 957 defer id.DumpIndex(t) 958 pn1 := id.NewPermanode() 959 t.Logf("uploaded permanode %q", pn1) 960 cl1 := id.SetAttribute(pn1, "tag", "foo1") 961 cl1Time := id.lastTime() 962 t.Logf("set attribute %q", cl1) 963 964 // delete pn1 965 delpn1 := id.Delete(pn1) 966 t.Logf("del claim %q deletes %q", delpn1, pn1) 967 deleted := idx.IsDeleted(pn1) 968 if !deleted { 969 t.Fatal("pn1 should be deleted") 970 } 971 972 // and try to find it with SearchPermanodesWithAttr (which should not work) 973 { 974 ch := make(chan blob.Ref, 10) 975 req := &camtypes.PermanodeByAttrRequest{ 976 Signer: id.SignerBlobRef, 977 Attribute: "tag", 978 Query: "foo1"} 979 err := id.Index.SearchPermanodesWithAttr(ch, req) 980 if err != nil { 981 t.Fatalf("SearchPermanodesWithAttr = %v", err) 982 } 983 var got []blob.Ref 984 for r := range ch { 985 got = append(got, r) 986 } 987 want := []blob.Ref{} 988 if len(got) != len(want) { 989 t.Errorf("id.Index.SearchPermanodesWithAttr gives %q, want %q", got, want) 990 } 991 } 992 993 // delete pn1 again with another claim 994 delpn1bis := id.Delete(pn1) 995 t.Logf("del claim %q deletes %q a second time", delpn1bis, pn1) 996 deleted = idx.IsDeleted(pn1) 997 if !deleted { 998 t.Fatal("pn1 should be deleted") 999 } 1000 1001 // verify that deleting delpn1 is not enough to make pn1 undeleted 1002 del2 := id.Delete(delpn1) 1003 t.Logf("delete claim %q deletes %q, which should not yet revive %q", del2, delpn1, pn1) 1004 deleted = idx.IsDeleted(pn1) 1005 if !deleted { 1006 t.Fatal("pn1 should not yet be undeleted") 1007 } 1008 // we should not yet be able to find it again with SearchPermanodesWithAttr 1009 { 1010 ch := make(chan blob.Ref, 10) 1011 req := &camtypes.PermanodeByAttrRequest{ 1012 Signer: id.SignerBlobRef, 1013 Attribute: "tag", 1014 Query: "foo1"} 1015 err := id.Index.SearchPermanodesWithAttr(ch, req) 1016 if err != nil { 1017 t.Fatalf("SearchPermanodesWithAttr = %v", err) 1018 } 1019 var got []blob.Ref 1020 for r := range ch { 1021 got = append(got, r) 1022 } 1023 want := []blob.Ref{} 1024 if len(got) != len(want) { 1025 t.Errorf("id.Index.SearchPermanodesWithAttr gives %q, want %q", got, want) 1026 } 1027 } 1028 1029 // delete delpn1bis as well -> should undelete pn1 1030 del2bis := id.Delete(delpn1bis) 1031 t.Logf("delete claim %q deletes %q, which should revive %q", del2bis, delpn1bis, pn1) 1032 deleted = idx.IsDeleted(pn1) 1033 if deleted { 1034 t.Fatal("pn1 should be undeleted") 1035 } 1036 // we should now be able to find it again with SearchPermanodesWithAttr 1037 { 1038 ch := make(chan blob.Ref, 10) 1039 req := &camtypes.PermanodeByAttrRequest{ 1040 Signer: id.SignerBlobRef, 1041 Attribute: "tag", 1042 Query: "foo1"} 1043 err := id.Index.SearchPermanodesWithAttr(ch, req) 1044 if err != nil { 1045 t.Fatalf("SearchPermanodesWithAttr = %v", err) 1046 } 1047 var got []blob.Ref 1048 for r := range ch { 1049 got = append(got, r) 1050 } 1051 want := []blob.Ref{pn1} 1052 if len(got) < 1 || got[0].String() != want[0].String() { 1053 t.Errorf("id.Index.SearchPermanodesWithAttr gives %q, want %q", got, want) 1054 } 1055 } 1056 1057 // Delete cl1 1058 del3 := id.Delete(cl1) 1059 t.Logf("del claim %q deletes claim %q", del3, cl1) 1060 deleted = idx.IsDeleted(cl1) 1061 if !deleted { 1062 t.Fatal("cl1 should be deleted") 1063 } 1064 // we should not find anything with SearchPermanodesWithAttr 1065 { 1066 ch := make(chan blob.Ref, 10) 1067 req := &camtypes.PermanodeByAttrRequest{ 1068 Signer: id.SignerBlobRef, 1069 Attribute: "tag", 1070 Query: "foo1"} 1071 err := id.Index.SearchPermanodesWithAttr(ch, req) 1072 if err != nil { 1073 t.Fatalf("SearchPermanodesWithAttr = %v", err) 1074 } 1075 var got []blob.Ref 1076 for r := range ch { 1077 got = append(got, r) 1078 } 1079 want := []blob.Ref{} 1080 if len(got) != len(want) { 1081 t.Errorf("id.Index.SearchPermanodesWithAttr gives %q, want %q", got, want) 1082 } 1083 } 1084 // and now check that AppendClaims finds nothing for pn 1085 { 1086 claims, err := id.Index.AppendClaims(nil, pn1, id.SignerBlobRef, "") 1087 if err != nil { 1088 t.Errorf("AppendClaims = %v", err) 1089 } else { 1090 want := []camtypes.Claim{} 1091 if len(claims) != len(want) { 1092 t.Errorf("id.Index.AppendClaims gives %q, want %q", claims, want) 1093 } 1094 } 1095 } 1096 1097 // undelete cl1 1098 del4 := id.Delete(del3) 1099 t.Logf("del claim %q deletes del claim %q, which should undelete %q", del4, del3, cl1) 1100 // We should now be able to find it again with both methods 1101 { 1102 ch := make(chan blob.Ref, 10) 1103 req := &camtypes.PermanodeByAttrRequest{ 1104 Signer: id.SignerBlobRef, 1105 Attribute: "tag", 1106 Query: "foo1"} 1107 err := id.Index.SearchPermanodesWithAttr(ch, req) 1108 if err != nil { 1109 t.Fatalf("SearchPermanodesWithAttr = %v", err) 1110 } 1111 var got []blob.Ref 1112 for r := range ch { 1113 got = append(got, r) 1114 } 1115 want := []blob.Ref{pn1} 1116 if len(got) < 1 || got[0].String() != want[0].String() { 1117 t.Errorf("id.Index.SearchPermanodesWithAttr gives %q, want %q", got, want) 1118 } 1119 } 1120 // and check that AppendClaims finds cl1, with the right modtime too 1121 { 1122 claims, err := id.Index.AppendClaims(nil, pn1, id.SignerBlobRef, "") 1123 if err != nil { 1124 t.Errorf("AppendClaims = %v", err) 1125 } else { 1126 want := []camtypes.Claim{ 1127 camtypes.Claim{ 1128 BlobRef: cl1, 1129 Permanode: pn1, 1130 Signer: id.SignerBlobRef, 1131 Date: cl1Time.UTC(), 1132 Type: "set-attribute", 1133 Attr: "tag", 1134 Value: "foo1", 1135 }, 1136 } 1137 if !reflect.DeepEqual(claims, want) { 1138 t.Errorf("GetOwnerClaims results differ.\n got: %v\nwant: %v", 1139 claims, want) 1140 } 1141 } 1142 } 1143 } 1144 1145 type searchResults []camtypes.RecentPermanode 1146 1147 func (s searchResults) String() string { 1148 var buf bytes.Buffer 1149 fmt.Fprintf(&buf, "[%d search results: ", len(s)) 1150 for _, r := range s { 1151 fmt.Fprintf(&buf, "{BlobRef: %s, Signer: %s, LastModTime: %d}", 1152 r.Permanode, r.Signer, r.LastModTime.Unix()) 1153 } 1154 buf.WriteString("]") 1155 return buf.String() 1156 }