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