github.com/cayleygraph/cayley@v0.7.7/schema/loader_test.go (about)

     1  package schema_test
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/cayleygraph/cayley/graph"
     8  	"github.com/cayleygraph/cayley/graph/iterator"
     9  	"github.com/cayleygraph/cayley/graph/memstore"
    10  	"github.com/cayleygraph/cayley/schema"
    11  	"github.com/cayleygraph/quad"
    12  )
    13  
    14  func TestLoadLoop(t *testing.T) {
    15  	sch := schema.NewConfig()
    16  
    17  	a := &NodeLoop{ID: iri("A"), Name: "Node A"}
    18  	a.Next = a
    19  
    20  	qs := memstore.New([]quad.Quad{
    21  		{a.ID, iri("name"), quad.String(a.Name), nil},
    22  		{a.ID, iri("next"), a.ID, nil},
    23  	}...)
    24  
    25  	b := &NodeLoop{}
    26  	if err := sch.LoadIteratorTo(nil, qs, reflect.ValueOf(b), nil); err != nil {
    27  		t.Error(err)
    28  		return
    29  	}
    30  	if a.ID != b.ID || a.Name != b.Name {
    31  		t.Fatalf("%#v vs %#v", a, b)
    32  	}
    33  	if b != b.Next {
    34  		t.Fatalf("loop is broken: %p vs %p", b, b.Next)
    35  	}
    36  
    37  	a = &NodeLoop{ID: iri("A"), Name: "Node A"}
    38  	b = &NodeLoop{ID: iri("B"), Name: "Node B"}
    39  	c := &NodeLoop{ID: iri("C"), Name: "Node C"}
    40  	a.Next = b
    41  	b.Next = c
    42  	c.Next = a
    43  
    44  	qs = memstore.New([]quad.Quad{
    45  		{a.ID, iri("name"), quad.String(a.Name), nil},
    46  		{b.ID, iri("name"), quad.String(b.Name), nil},
    47  		{c.ID, iri("name"), quad.String(c.Name), nil},
    48  		{a.ID, iri("next"), b.ID, nil},
    49  		{b.ID, iri("next"), c.ID, nil},
    50  		{c.ID, iri("next"), a.ID, nil},
    51  	}...)
    52  
    53  	a1 := &NodeLoop{}
    54  	if err := sch.LoadIteratorTo(nil, qs, reflect.ValueOf(a1), nil); err != nil {
    55  		t.Error(err)
    56  		return
    57  	}
    58  	if a.ID != a1.ID || a.Name != a1.Name {
    59  		t.Fatalf("%#v vs %#v", a, b)
    60  	}
    61  	b1 := a1.Next
    62  	c1 := b1.Next
    63  	if b.ID != b1.ID || b.Name != b1.Name {
    64  		t.Fatalf("%#v vs %#v", a, b)
    65  	}
    66  	if c.ID != c1.ID || c.Name != c1.Name {
    67  		t.Fatalf("%#v vs %#v", a, b)
    68  	}
    69  	if a1 != c1.Next {
    70  		t.Fatalf("loop is broken: %p vs %p", a1, c1.Next)
    71  	}
    72  }
    73  
    74  func TestLoadIteratorTo(t *testing.T) {
    75  	sch := schema.NewConfig()
    76  	for i, c := range testFillValueCases {
    77  		t.Run(c.name, func(t *testing.T) {
    78  			qs := memstore.New(c.quads...)
    79  			rt := reflect.TypeOf(c.expect)
    80  			var out reflect.Value
    81  			if rt.Kind() == reflect.Ptr {
    82  				out = reflect.New(rt.Elem())
    83  			} else {
    84  				out = reflect.New(rt)
    85  			}
    86  			var it graph.Iterator
    87  			if c.from != nil {
    88  				fixed := iterator.NewFixed()
    89  				for _, id := range c.from {
    90  					fixed.Add(qs.ValueOf(id))
    91  				}
    92  				it = fixed
    93  			}
    94  			depth := c.depth
    95  			if depth == 0 {
    96  				depth = -1
    97  			}
    98  			if err := sch.LoadIteratorToDepth(nil, qs, out, depth, it); err != nil {
    99  				t.Errorf("case %d failed: %v", i+1, err)
   100  				return
   101  			}
   102  			var got interface{}
   103  			if rt.Kind() == reflect.Ptr {
   104  				got = out.Interface()
   105  			} else {
   106  				got = out.Elem().Interface()
   107  			}
   108  			if s, ok := got.(interface {
   109  				Sort()
   110  			}); ok {
   111  				s.Sort()
   112  			}
   113  			if s, ok := c.expect.(interface {
   114  				Sort()
   115  			}); ok {
   116  				s.Sort()
   117  			}
   118  			if !reflect.DeepEqual(got, c.expect) {
   119  				t.Errorf("case %d failed: objects are different\n%#v\n%#v",
   120  					i+1, got, c.expect,
   121  				)
   122  			}
   123  		})
   124  	}
   125  }
   126  
   127  var testFillValueCases = []struct {
   128  	name   string
   129  	expect interface{}
   130  	quads  []quad.Quad
   131  	depth  int
   132  	from   []quad.Value
   133  }{
   134  	{
   135  		name: "complex object",
   136  		expect: struct {
   137  			rdfType struct{} `quad:"rdf:type > some:Type"`
   138  			ID      quad.IRI `quad:"@id"`
   139  			Name    string   `quad:"name"`
   140  			Values  []string `quad:"values"`
   141  			Items   []item   `quad:"items"`
   142  			Sub     *item    `quad:"sub"`
   143  			Val     int      `quad:"val"`
   144  		}{
   145  			ID:     "1234",
   146  			Name:   "some item",
   147  			Values: []string{"val1", "val2"},
   148  			Items: []item{
   149  				{ID: "sub1", Name: "Sub 1"},
   150  				{ID: "sub2", Name: "Sub 2"},
   151  			},
   152  			Sub: &item{ID: "sub3", Name: "Sub 3"},
   153  			Val: 123,
   154  		},
   155  		quads: []quad.Quad{
   156  			{iri("1234"), typeIRI, iri("some:Type"), nil},
   157  			{iri("1234"), iri("name"), quad.String("some item"), nil},
   158  			{iri("1234"), iri("values"), quad.String("val1"), nil},
   159  			{iri("1234"), iri("values"), quad.String("val2"), nil},
   160  			{iri("sub1"), typeIRI, iri("some:item"), nil},
   161  			{iri("sub1"), iri("name"), quad.String("Sub 1"), nil},
   162  			{iri("1234"), iri("items"), iri("sub1"), nil},
   163  			{iri("sub2"), typeIRI, iri("some:item"), nil},
   164  			{iri("sub2"), iri("name"), quad.String("Sub 2"), nil},
   165  			{iri("1234"), iri("items"), iri("sub2"), nil},
   166  			{iri("sub3"), typeIRI, iri("some:item"), nil},
   167  			{iri("sub3"), iri("name"), quad.String("Sub 3"), nil},
   168  			{iri("1234"), iri("sub"), iri("sub3"), nil},
   169  			{iri("1234"), iri("val"), quad.Int(123), nil},
   170  		},
   171  	},
   172  	{
   173  		name: "complex object (id value)",
   174  		expect: struct {
   175  			rdfType struct{}   `quad:"rdf:type > some:Type"`
   176  			ID      quad.Value `quad:"@id"`
   177  			Name    string     `quad:"name"`
   178  			Values  []string   `quad:"values"`
   179  			Items   []item     `quad:"items"`
   180  		}{
   181  			ID:     quad.BNode("1234"),
   182  			Name:   "some item",
   183  			Values: []string{"val1", "val2"},
   184  			Items: []item{
   185  				{ID: "sub1", Name: "Sub 1"},
   186  				{ID: "sub2", Name: "Sub 2"},
   187  			},
   188  		},
   189  		quads: []quad.Quad{
   190  			{quad.BNode("1234"), typeIRI, iri("some:Type"), nil},
   191  			{quad.BNode("1234"), iri("name"), quad.String("some item"), nil},
   192  			{quad.BNode("1234"), iri("values"), quad.String("val1"), nil},
   193  			{quad.BNode("1234"), iri("values"), quad.String("val2"), nil},
   194  			{iri("sub1"), typeIRI, iri("some:item"), nil},
   195  			{iri("sub1"), iri("name"), quad.String("Sub 1"), nil},
   196  			{quad.BNode("1234"), iri("items"), iri("sub1"), nil},
   197  			{iri("sub2"), typeIRI, iri("some:item"), nil},
   198  			{iri("sub2"), iri("name"), quad.String("Sub 2"), nil},
   199  			{quad.BNode("1234"), iri("items"), iri("sub2"), nil},
   200  		},
   201  	},
   202  	{
   203  		name: "embedded object",
   204  		expect: struct {
   205  			rdfType struct{} `quad:"rdf:type > some:Type"`
   206  			item2
   207  			ID     quad.IRI `quad:"@id"`
   208  			Values []string `quad:"values"`
   209  		}{
   210  			item2:  item2{Name: "Sub 1", Spec: "special"},
   211  			ID:     "1234",
   212  			Values: []string{"val1", "val2"},
   213  		},
   214  		quads: []quad.Quad{
   215  			{iri("1234"), typeIRI, iri("some:Type"), nil},
   216  			{iri("1234"), iri("name"), quad.String("Sub 1"), nil},
   217  			{iri("1234"), iri("spec"), quad.String("special"), nil},
   218  			{iri("1234"), iri("values"), quad.String("val1"), nil},
   219  			{iri("1234"), iri("values"), quad.String("val2"), nil},
   220  		},
   221  	},
   222  	{
   223  		name: "type shorthand",
   224  		expect: struct {
   225  			rdfType struct{} `quad:"@type > some:Type"`
   226  			item2
   227  			ID     quad.IRI `quad:"@id"`
   228  			Values []string `quad:"values"`
   229  		}{
   230  			item2:  item2{Name: "Sub 1", Spec: "special"},
   231  			ID:     "1234",
   232  			Values: []string{"val1", "val2"},
   233  		},
   234  		quads: []quad.Quad{
   235  			{iri("1234"), typeIRI, iri("some:Type"), nil},
   236  			{iri("1234"), iri("name"), quad.String("Sub 1"), nil},
   237  			{iri("1234"), iri("spec"), quad.String("special"), nil},
   238  			{iri("1234"), iri("values"), quad.String("val1"), nil},
   239  			{iri("1234"), iri("values"), quad.String("val2"), nil},
   240  		},
   241  	},
   242  	{
   243  		name: "tree",
   244  		expect: treeItem{
   245  			ID:   iri("n1"),
   246  			Name: "Node 1",
   247  			Children: []treeItem{
   248  				{
   249  					ID:   iri("n2"),
   250  					Name: "Node 2",
   251  				},
   252  				{
   253  					ID:   iri("n3"),
   254  					Name: "Node 3",
   255  					Children: []treeItem{
   256  						{
   257  							ID:   iri("n4"),
   258  							Name: "Node 4",
   259  						},
   260  					},
   261  				},
   262  			},
   263  		},
   264  		quads: treeQuads,
   265  		from:  []quad.Value{iri("n1")},
   266  	},
   267  	{
   268  		name: "tree with depth limit 1",
   269  		expect: treeItem{
   270  			ID:   iri("n1"),
   271  			Name: "Node 1",
   272  			Children: []treeItem{
   273  				{
   274  					ID:   iri("n2"),
   275  					Name: "Node 2",
   276  				},
   277  				{
   278  					ID:   iri("n3"),
   279  					Name: "Node 3",
   280  					Children: []treeItem{
   281  						{
   282  							ID: iri("n4"),
   283  						},
   284  					},
   285  				},
   286  			},
   287  		},
   288  		depth: 1,
   289  		quads: treeQuads,
   290  		from:  []quad.Value{iri("n1")},
   291  	},
   292  	{
   293  		name: "tree with depth limit 2",
   294  		expect: treeItemOpt{
   295  			ID:   iri("n1"),
   296  			Name: "Node 1",
   297  			Children: []treeItemOpt{
   298  				{
   299  					ID:   iri("n2"),
   300  					Name: "Node 2",
   301  				},
   302  				{
   303  					ID:   iri("n3"),
   304  					Name: "Node 3",
   305  					Children: []treeItemOpt{
   306  						{
   307  							ID:   iri("n4"),
   308  							Name: "Node 4",
   309  						},
   310  					},
   311  				},
   312  			},
   313  		},
   314  		depth: 2,
   315  		quads: treeQuads,
   316  		from:  []quad.Value{iri("n1")},
   317  	},
   318  	{
   319  		name: "tree with required children",
   320  		expect: treeItemReq{
   321  			ID:   iri("n1"),
   322  			Name: "Node 1",
   323  			Children: []treeItemReq{
   324  				{
   325  					ID:   iri("n3"),
   326  					Name: "Node 3",
   327  					// TODO(dennwc): a strange behavior: this field is required, but it's empty for current object,
   328  					// because all it's children are missing the same field. Leaving this as-is for now because
   329  					// it's weird to set Children field as required in a tree.
   330  					Children: nil,
   331  				},
   332  			},
   333  		},
   334  		quads: treeQuads,
   335  		from:  []quad.Value{iri("n1")},
   336  	},
   337  	{
   338  		name: "simple object",
   339  		expect: subObject{
   340  			genObject: genObject{
   341  				ID:   "1234",
   342  				Name: "Obj",
   343  			},
   344  			Num: 3,
   345  		},
   346  		quads: []quad.Quad{
   347  			{iri("1234"), iri("name"), quad.String("Obj"), nil},
   348  			{iri("1234"), iri("num"), quad.Int(3), nil},
   349  		},
   350  	},
   351  	{
   352  		name: "typedef",
   353  		expect: genObjectTypedef{
   354  			ID:   "1234",
   355  			Name: "Obj",
   356  		},
   357  		quads: []quad.Quad{
   358  			{iri("1234"), iri("name"), quad.String("Obj"), nil},
   359  		},
   360  	},
   361  	{
   362  		name:   "coords",
   363  		expect: Coords{Lat: 12.3, Lng: 34.5},
   364  		quads: []quad.Quad{
   365  			{iri("c1"), typeIRI, iri("ex:Coords"), nil},
   366  			{iri("c1"), iri("ex:lat"), quad.Float(12.3), nil},
   367  			{iri("c1"), iri("ex:lng"), quad.Float(34.5), nil},
   368  		},
   369  	},
   370  	{
   371  		name: "same node",
   372  		expect: NestedNode{
   373  			ID:   "c1",
   374  			Name: "A",
   375  			Prev: genObject{
   376  				ID:   "c2",
   377  				Name: "B",
   378  			},
   379  			Next: genObject{
   380  				ID:   "c2",
   381  				Name: "B",
   382  			},
   383  		},
   384  		quads: []quad.Quad{
   385  			{iri("c1"), iri("name"), quad.String("A"), nil},
   386  			{iri("c2"), iri("name"), quad.String("B"), nil},
   387  			{iri("c1"), iri("next"), iri("c2"), nil},
   388  			{iri("c1"), iri("prev"), iri("c2"), nil},
   389  		},
   390  	},
   391  	{
   392  		name: "all optional",
   393  		expect: Alts{
   394  			Alt: []OptFields{
   395  				{One: "A"},
   396  				{Two: "B"},
   397  				{One: "C", Two: "D"},
   398  			},
   399  		},
   400  		quads: []quad.Quad{
   401  			{iri("c1"), iri("alt"), iri("h1"), nil},
   402  			{iri("c1"), iri("alt"), iri("h2"), nil},
   403  			{iri("c1"), iri("alt"), iri("h3"), nil},
   404  
   405  			{iri("h1"), iri("one"), quad.String("A"), nil},
   406  			{iri("h2"), iri("two"), quad.String("B"), nil},
   407  			{iri("h3"), iri("one"), quad.String("C"), nil},
   408  			{iri("h3"), iri("two"), quad.String("D"), nil},
   409  		},
   410  	},
   411  }