github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/schema/schema_test.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 schema 18 19 import ( 20 "encoding/json" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "reflect" 25 "strings" 26 "testing" 27 "time" 28 29 "camlistore.org/pkg/blob" 30 . "camlistore.org/pkg/test/asserts" 31 ) 32 33 const kExpectedHeader = `{"camliVersion"` 34 35 func TestJSON(t *testing.T) { 36 fileName := "schema_test.go" 37 fi, _ := os.Lstat(fileName) 38 m := NewCommonFileMap(fileName, fi) 39 json, err := m.JSON() 40 if err != nil { 41 t.Fatalf("Unexpected error: %v", err) 42 } 43 t.Logf("Got json: [%s]\n", json) 44 // TODO: test it parses back 45 46 if !strings.HasPrefix(json, kExpectedHeader) { 47 t.Errorf("JSON does't start with expected header.") 48 } 49 50 } 51 52 func TestRegularFile(t *testing.T) { 53 fileName := "schema_test.go" 54 fi, err := os.Lstat(fileName) 55 AssertNil(t, err, "schema_test.go stat") 56 m := NewCommonFileMap("schema_test.go", fi) 57 json, err := m.JSON() 58 if err != nil { 59 t.Fatalf("Unexpected error: %v", err) 60 } 61 t.Logf("Got json for regular file: [%s]\n", json) 62 } 63 64 func TestSymlink(t *testing.T) { 65 // We create the symlink now because make.go does not mirror 66 // symlinks properly, and it is less intrusive to do that here. 67 defer os.RemoveAll("testdata") 68 err := os.Mkdir("testdata", 0755) 69 AssertNil(t, err, "Mkdir") 70 err = os.Chdir("testdata") 71 AssertNil(t, err, "Chdir") 72 err = os.Symlink("test-target", "test-symlink") 73 AssertNil(t, err, "creating test-symlink") 74 err = os.Chdir("..") 75 AssertNil(t, err, "Chdir") 76 fileName := filepath.Join("testdata", "test-symlink") 77 fi, err := os.Lstat(fileName) 78 AssertNil(t, err, "test-symlink stat") 79 m := NewCommonFileMap(fileName, fi) 80 json, err := m.JSON() 81 if err != nil { 82 t.Fatalf("Unexpected error: %v", err) 83 } 84 t.Logf("Got json for symlink file: [%s]\n", json) 85 } 86 87 func TestUtf8StrLen(t *testing.T) { 88 tests := []struct { 89 in string 90 want int 91 }{ 92 {"", 0}, 93 {"a", 1}, 94 {"foo", 3}, 95 {"Здравствуйте!", 25}, 96 {"foo\x80", 3}, 97 {"\x80foo", 0}, 98 } 99 for _, tt := range tests { 100 got := utf8StrLen(tt.in) 101 if got != tt.want { 102 t.Errorf("utf8StrLen(%q) = %v; want %v", tt.in, got, tt.want) 103 } 104 } 105 } 106 107 func TestMixedArrayFromString(t *testing.T) { 108 b80 := byte('\x80') 109 tests := []struct { 110 in string 111 want []interface{} 112 }{ 113 {"foo", []interface{}{"foo"}}, 114 {"\x80foo", []interface{}{b80, "foo"}}, 115 {"foo\x80foo", []interface{}{"foo", b80, "foo"}}, 116 {"foo\x80", []interface{}{"foo", b80}}, 117 {"\x80", []interface{}{b80}}, 118 {"\x80\x80", []interface{}{b80, b80}}, 119 } 120 for _, tt := range tests { 121 got := mixedArrayFromString(tt.in) 122 if !reflect.DeepEqual(got, tt.want) { 123 t.Errorf("mixedArrayFromString(%q) = %#v; want %#v", tt.in, got, tt.want) 124 } 125 } 126 } 127 128 type mixPartsTest struct { 129 json, expected string 130 } 131 132 func TestStringFromMixedArray(t *testing.T) { 133 tests := []mixPartsTest{ 134 {`["brad"]`, "brad"}, 135 {`["brad", 32, 70]`, "brad F"}, 136 {`["brad", "fitz"]`, "bradfitz"}, 137 {`["Am", 233, "lie.jpg"]`, "Am\xe9lie.jpg"}, 138 } 139 for idx, test := range tests { 140 var v []interface{} 141 if err := json.Unmarshal([]byte(test.json), &v); err != nil { 142 t.Fatalf("invalid JSON in test %d", idx) 143 } 144 got := stringFromMixedArray(v) 145 if got != test.expected { 146 t.Errorf("test %d got %q; expected %q", idx, got, test.expected) 147 } 148 } 149 } 150 151 func TestRFC3339(t *testing.T) { 152 tests := []string{ 153 "2012-05-13T15:02:47Z", 154 "2012-05-13T15:02:47.1234Z", 155 "2012-05-13T15:02:47.123456789Z", 156 } 157 for _, in := range tests { 158 tm, err := time.Parse(time.RFC3339, in) 159 if err != nil { 160 t.Errorf("error parsing %q", in) 161 continue 162 } 163 if out := RFC3339FromTime(tm); in != out { 164 t.Errorf("RFC3339FromTime(%q) = %q; want %q", in, out, in) 165 } 166 } 167 } 168 169 func TestBlobFromReader(t *testing.T) { 170 br := blob.MustParse("sha1-f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") 171 blob, err := BlobFromReader(br, strings.NewReader(`{"camliVersion": 1, "camliType": "foo"} `)) 172 if err != nil { 173 t.Error(err) 174 } else if blob.Type() != "foo" { 175 t.Errorf("got type %q; want foo", blob.Type()) 176 } 177 178 blob, err = BlobFromReader(br, strings.NewReader(`{"camliVersion": 1, "camliType": "foo"} X `)) 179 if err == nil { 180 // TODO(bradfitz): fix this somehow. Currently encoding/json's 181 // decoder over-reads. 182 // See: https://code.google.com/p/go/issues/detail?id=1955 , 183 // which was "fixed", but not really. 184 t.Logf("TODO(bradfitz): make sure bogus non-whitespace after the JSON object causes an error.") 185 } 186 } 187 188 func TestAttribute(t *testing.T) { 189 tm := time.Unix(123, 456) 190 br := blob.MustParse("xxx-1234") 191 tests := []struct { 192 bb *Builder 193 want string 194 }{ 195 { 196 bb: NewSetAttributeClaim(br, "attr1", "val1"), 197 want: `{"camliVersion": 1, 198 "attribute": "attr1", 199 "camliType": "claim", 200 "claimDate": "1970-01-01T00:02:03.000000456Z", 201 "claimType": "set-attribute", 202 "permaNode": "xxx-1234", 203 "value": "val1" 204 }`, 205 }, 206 { 207 bb: NewAddAttributeClaim(br, "tag", "funny"), 208 want: `{"camliVersion": 1, 209 "attribute": "tag", 210 "camliType": "claim", 211 "claimDate": "1970-01-01T00:02:03.000000456Z", 212 "claimType": "add-attribute", 213 "permaNode": "xxx-1234", 214 "value": "funny" 215 }`, 216 }, 217 { 218 bb: NewDelAttributeClaim(br, "attr1", "val1"), 219 want: `{"camliVersion": 1, 220 "attribute": "attr1", 221 "camliType": "claim", 222 "claimDate": "1970-01-01T00:02:03.000000456Z", 223 "claimType": "del-attribute", 224 "permaNode": "xxx-1234", 225 "value": "val1" 226 }`, 227 }, 228 { 229 bb: NewDelAttributeClaim(br, "attr2", ""), 230 want: `{"camliVersion": 1, 231 "attribute": "attr2", 232 "camliType": "claim", 233 "claimDate": "1970-01-01T00:02:03.000000456Z", 234 "claimType": "del-attribute", 235 "permaNode": "xxx-1234" 236 }`, 237 }, 238 { 239 bb: newClaim(&claimParam{ 240 permanode: br, 241 claimType: SetAttributeClaim, 242 attribute: "foo", 243 value: "bar", 244 }, &claimParam{ 245 permanode: br, 246 claimType: DelAttributeClaim, 247 attribute: "foo", 248 value: "specific-del", 249 }, &claimParam{ 250 permanode: br, 251 claimType: DelAttributeClaim, 252 attribute: "foo", 253 }), 254 want: `{"camliVersion": 1, 255 "camliType": "claim", 256 "claimDate": "1970-01-01T00:02:03.000000456Z", 257 "claimType": "multi", 258 "claims": [ 259 { 260 "attribute": "foo", 261 "claimType": "set-attribute", 262 "permaNode": "xxx-1234", 263 "value": "bar" 264 }, 265 { 266 "attribute": "foo", 267 "claimType": "del-attribute", 268 "permaNode": "xxx-1234", 269 "value": "specific-del" 270 }, 271 { 272 "attribute": "foo", 273 "claimType": "del-attribute", 274 "permaNode": "xxx-1234" 275 } 276 ] 277 }`, 278 }, 279 } 280 for i, tt := range tests { 281 tt.bb.SetClaimDate(tm) 282 got, err := tt.bb.JSON() 283 if err != nil { 284 t.Errorf("%d. JSON error = %v", i, err) 285 continue 286 } 287 if got != tt.want { 288 t.Errorf("%d.\t got:\n%s\n\twant:q\n%s", i, got, tt.want) 289 } 290 } 291 } 292 293 func TestDeleteClaim(t *testing.T) { 294 tm := time.Unix(123, 456) 295 br := blob.MustParse("xxx-1234") 296 delTest := struct { 297 bb *Builder 298 want string 299 }{ 300 bb: NewDeleteClaim(br), 301 want: `{"camliVersion": 1, 302 "camliType": "claim", 303 "claimDate": "1970-01-01T00:02:03.000000456Z", 304 "claimType": "delete", 305 "target": "xxx-1234" 306 }`, 307 } 308 delTest.bb.SetClaimDate(tm) 309 got, err := delTest.bb.JSON() 310 if err != nil { 311 t.Fatalf("JSON error = %v", err) 312 } 313 if got != delTest.want { 314 t.Fatalf("got:\n%s\n\twant:q\n%s", got, delTest.want) 315 } 316 } 317 318 func TestAsClaimAndAsShare(t *testing.T) { 319 br := blob.MustParse("xxx-1234") 320 signer := blob.MustParse("yyy-5678") 321 322 bb := NewSetAttributeClaim(br, "title", "Test Title") 323 bb = bb.SetSigner(signer) 324 bb = bb.SetClaimDate(time.Now()) 325 c1 := bb.Blob() 326 c1.ss.Sig = "non-null-sig" // required by AsShare 327 328 bb = NewShareRef(ShareHaveRef, br, true) 329 bb = bb.SetSigner(signer) 330 bb = bb.SetClaimDate(time.Now()) 331 c2 := bb.Blob() 332 c2.ss.Sig = "non-null-sig" // required by AsShare 333 334 if !br.Valid() { 335 t.Error("Blobref not valid") 336 } 337 338 _, ok := c1.AsClaim() 339 if !ok { 340 t.Error("Claim 1 not returned as claim") 341 } 342 343 _, ok = c2.AsClaim() 344 if !ok { 345 t.Error("Claim 2 not returned as claim") 346 } 347 348 s, ok := c1.AsShare() 349 if ok { 350 t.Error("Title claim returned share", s) 351 } 352 353 s, ok = c2.AsShare() 354 if !ok { 355 t.Error("Share claim failed to return share") 356 } 357 } 358 359 func TestShareExpiration(t *testing.T) { 360 defer func() { clockNow = time.Now }() 361 b, err := BlobFromReader( 362 blob.MustParse("sha1-64ffa72fa9bcb2f825e7ed40b9451e5cadca4c2c"), 363 strings.NewReader(`{"camliVersion": 1, 364 "authType": "haveref", 365 "camliSigner": "sha1-f2b0b7da718b97ce8c31591d8ed4645c777f3ef4", 366 "camliType": "claim", 367 "claimDate": "2013-09-08T23:58:53.656549677Z", 368 "claimType": "share", 369 "expires": "2013-09-09T23:58:53.65658012Z", 370 "target": "sha1-f1d2d2f924e986ac86fdf7b36c94bcdf32beec15", 371 "transitive": false 372 ,"camliSig":"wsBcBAABCAAQBQJSLQ89CRApMaZ8JvWr2gAAcuEIABRQolhn+yKksfaBx6oLo18NWvWQ+aYweF+5Gu0TH0Ixur7t1o5HFtFSSfFISyggSZDJSjsxoxaawhWrvCe9dZuU2s/zgRpgUtd2xmBt82tLOn9JidnUavsNGFXbfCwdUBSkzN0vDYLmgXW0VtiybB354uIKfOInZor2j8Mq0p6pkWzK3qq9W0dku7iE96YFaTb4W7eOikqoSC6VpjC1/4MQWOYRHLcPcIEY6xJ8es2sYMMSNXuVaR9nMupz8ZcTygP4jh+lPR1OH61q/FSjpRp7GKt4wZ1PknYjMbnpIzVjiSz0MkYd65bpZwuPOwZh/h2kHW7wvHNQZfWUJHEsOAI==J2ID"}`), 373 ) 374 if err != nil { 375 t.Fatal(err) 376 } 377 s, ok := b.AsShare() 378 if !ok { 379 t.Fatal("expected share") 380 } 381 clockNow = func() time.Time { return time.Unix(100, 0) } 382 if s.IsExpired() { 383 t.Error("expected not expired") 384 } 385 clockNow = func() time.Time { return time.Unix(1378687181+2*86400, 0) } 386 if !s.IsExpired() { 387 t.Error("expected expired") 388 } 389 390 // And without an expiration time: 391 b, err = BlobFromReader( 392 blob.MustParse("sha1-931875ec6b8d917b7aae9f672f4f92de1ffaeeb1"), 393 strings.NewReader(`{"camliVersion": 1, 394 "authType": "haveref", 395 "camliSigner": "sha1-f2b0b7da718b97ce8c31591d8ed4645c777f3ef4", 396 "camliType": "claim", 397 "claimDate": "2013-09-09T01:01:09.907842963Z", 398 "claimType": "share", 399 "target": "sha1-64ffa72fa9bcb2f825e7ed40b9451e5cadca4c2c", 400 "transitive": false 401 ,"camliSig":"wsBcBAABCAAQBQJSLR3VCRApMaZ8JvWr2gAA14kIAKmi5rCI5JTBvHbBuAu7wPVA87BLXm/BaD6zjqOENB4U8B+6KxyuT6KXe9P591IDXdZmJTP5tesbLtKw0iAWiRf2ea0Y7Ms3K77nLnSZM5QIOzb4aQKd1668p/5KqU3VfNayoHt69YkXyKBkqyEPjHINzC03QuLz5NIEBMYJaNqKKtEtSgh4gG8BBYq5qQzdKFg/Hx7VhkhW1y/1wwGSFJjaiPFMIJsF4d/gaO01Ip7XLro63ccyCy81tqKHnVjv0uULmZdbpgd3RHGGSnW3c9BfqkGvc3Wl11UQKzqc9OT+WTAWp8TXg6bLES9sQNzerx2wUfjKB9J4Yrk14iBfjl8==AynO"}`), 402 ) 403 if err != nil { 404 t.Fatal(err) 405 } 406 s, ok = b.AsShare() 407 if !ok { 408 t.Fatal("expected share") 409 } 410 clockNow = func() time.Time { return time.Unix(100, 0) } 411 if s.IsExpired() { 412 t.Error("expected not expired") 413 } 414 clockNow = func() time.Time { return time.Unix(1378687181+2*86400, 0) } 415 if s.IsExpired() { 416 t.Error("expected not expired") 417 } 418 } 419 420 // camlistore.org/issue/305 421 func TestIssue305(t *testing.T) { 422 var in = `{"camliVersion": 1, 423 "camliType": "file", 424 "fileName": "2012-03-10 15.03.18.m4v", 425 "parts": [ 426 { 427 "bytesRef": "sha1-c76d8b17b887c207875e61a77b7eccc60289e61c", 428 "size": 20032564 429 } 430 ] 431 }` 432 var ss superset 433 if err := json.NewDecoder(strings.NewReader(in)).Decode(&ss); err != nil { 434 t.Fatal(err) 435 } 436 inref := blob.SHA1FromString(in) 437 blob, err := BlobFromReader(inref, strings.NewReader(in)) 438 if err != nil { 439 t.Fatal(err) 440 } 441 if blob.BlobRef() != inref { 442 t.Error("original ref = %s; want %s", blob.BlobRef(), inref) 443 } 444 bb := blob.Builder() 445 jback, err := bb.JSON() 446 if err != nil { 447 t.Fatal(err) 448 } 449 if jback != in { 450 t.Errorf("JSON doesn't match:\n got: %q\nwant: %q\n", jback, in) 451 } 452 out := bb.Blob() 453 if got := out.BlobRef(); got != inref { 454 t.Errorf("cloned ref = %v; want %v", got, inref) 455 } 456 } 457 458 func TestStaticFileAndStaticSymlink(t *testing.T) { 459 // TODO (marete): Split this into two test functions. 460 fd, err := ioutil.TempFile("", "schema-test-") 461 if err != nil { 462 t.Fatalf("io.TempFile(): %v", err) 463 } 464 defer os.Remove(fd.Name()) 465 defer fd.Close() 466 467 fi, err := os.Lstat(fd.Name()) 468 if err != nil { 469 t.Fatalf("os.Lstat(): %v", err) 470 } 471 472 bb := NewCommonFileMap(fd.Name(), fi) 473 bb.SetType("file") 474 bb.SetFileName(fd.Name()) 475 blob := bb.Blob() 476 477 sf, ok := blob.AsStaticFile() 478 if !ok { 479 t.Fatalf("Blob.AsStaticFile(): Unexpected return value: false") 480 } 481 if want, got := filepath.Base(fd.Name()), sf.FileName(); want != got { 482 t.Fatalf("StaticFile.FileName(): Expected %s, got %s", 483 want, got) 484 } 485 486 _, ok = sf.AsStaticSymlink() 487 if ok { 488 t.Fatalf("StaticFile.AsStaticSymlink(): Unexpected return value: true") 489 } 490 491 dir, err := ioutil.TempDir("", "schema-test-") 492 if err != nil { 493 t.Fatalf("ioutil.TempDir(): %v", err) 494 } 495 defer os.RemoveAll(dir) 496 497 target := "bar" 498 src := filepath.Join(dir, "foo") 499 err = os.Symlink(target, src) 500 fi, err = os.Lstat(src) 501 if err != nil { 502 t.Fatalf("os.Lstat(): %v", err) 503 } 504 505 bb = NewCommonFileMap(src, fi) 506 bb.SetType("symlink") 507 bb.SetFileName(src) 508 bb.SetSymlinkTarget(target) 509 blob = bb.Blob() 510 511 sf, ok = blob.AsStaticFile() 512 if !ok { 513 t.Fatalf("Blob.AsStaticFile(): Unexpected return value: false") 514 } 515 sl, ok := sf.AsStaticSymlink() 516 if !ok { 517 t.Fatalf("StaticFile.AsStaticSymlink(): Unexpected return value: false") 518 } 519 520 if want, got := filepath.Base(src), sl.FileName(); want != got { 521 t.Fatalf("StaticSymlink.FileName(): Expected %s, got %s", 522 want, got) 523 } 524 525 if want, got := target, sl.SymlinkTargetString(); got != want { 526 t.Fatalf("StaticSymlink.SymlinkTargetString(): Expected %s, got %s", want, got) 527 } 528 }