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  }