kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/serving/graph/columnar_test.go (about)

     1  /*
     2   * Copyright 2018 The Kythe Authors. All rights reserved.
     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 graph
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	"kythe.io/kythe/go/services/graph"
    24  	"kythe.io/kythe/go/serving/graph/columnar"
    25  	"kythe.io/kythe/go/storage/inmemory"
    26  	"kythe.io/kythe/go/storage/keyvalue"
    27  	"kythe.io/kythe/go/util/compare"
    28  	"kythe.io/kythe/go/util/kytheuri"
    29  	"kythe.io/kythe/go/util/schema/edges"
    30  	"kythe.io/kythe/go/util/schema/facts"
    31  	kinds "kythe.io/kythe/go/util/schema/nodes"
    32  
    33  	cpb "kythe.io/kythe/proto/common_go_proto"
    34  	gpb "kythe.io/kythe/proto/graph_go_proto"
    35  	gspb "kythe.io/kythe/proto/graph_serving_go_proto"
    36  	scpb "kythe.io/kythe/proto/schema_go_proto"
    37  	spb "kythe.io/kythe/proto/storage_go_proto"
    38  )
    39  
    40  func mustWriteEdges(t *testing.T, w keyvalue.Writer, e *gspb.Edges) {
    41  	kv, err := columnar.EncodeEdgesEntry(columnar.EdgesKeyPrefix, e)
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  	mustWrite(t, w, kv.Key, kv.Value)
    46  }
    47  
    48  func mustWrite(t *testing.T, w keyvalue.Writer, key, val []byte) {
    49  	if err := w.Write(key, val); err != nil {
    50  		t.Fatal(err)
    51  	}
    52  }
    53  
    54  func TestServingEdges(t *testing.T) {
    55  	ctx := context.Background()
    56  	db := inmemory.NewKeyValueDB()
    57  	w, err := db.Writer(ctx)
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	// Mark table as columnar
    63  	mustWrite(t, w, []byte(ColumnarTableKeyMarker), []byte{})
    64  
    65  	src := &spb.VName{Corpus: "corpus", Signature: "sig"}
    66  	edgeEntries := []*gspb.Edges{{
    67  		Source: src,
    68  		Entry: &gspb.Edges_Index_{&gspb.Edges_Index{
    69  			Node: &scpb.Node{
    70  				Kind: &scpb.Node_KytheKind{scpb.NodeKind_RECORD},
    71  				Fact: []*scpb.Fact{{
    72  					Name:  &scpb.Fact_KytheName{scpb.FactName_TEXT},
    73  					Value: []byte("value"),
    74  				}},
    75  			},
    76  		}},
    77  	}, {
    78  		Source: src,
    79  		Entry: &gspb.Edges_Edge_{&gspb.Edges_Edge{
    80  			Kind:   &gspb.Edges_Edge_KytheKind{scpb.EdgeKind_PARAM},
    81  			Target: &spb.VName{Signature: "param0"},
    82  		}},
    83  	}, {
    84  		Source: src,
    85  		Entry: &gspb.Edges_Edge_{&gspb.Edges_Edge{
    86  			Kind:    &gspb.Edges_Edge_KytheKind{scpb.EdgeKind_PARAM},
    87  			Ordinal: 1,
    88  			Target:  &spb.VName{Signature: "param1"},
    89  		}},
    90  	}, {
    91  		Source: src,
    92  		Entry: &gspb.Edges_Edge_{&gspb.Edges_Edge{
    93  			Kind:    &gspb.Edges_Edge_KytheKind{scpb.EdgeKind_CHILD_OF},
    94  			Reverse: true,
    95  			Target:  &spb.VName{Signature: "child1"},
    96  		}},
    97  	}, {
    98  		Source: src,
    99  		Entry: &gspb.Edges_Edge_{&gspb.Edges_Edge{
   100  			Kind:    &gspb.Edges_Edge_KytheKind{scpb.EdgeKind_CHILD_OF},
   101  			Reverse: true,
   102  			Target:  &spb.VName{Signature: "child2"},
   103  		}},
   104  	}, {
   105  		Source: src,
   106  		Entry: &gspb.Edges_Target_{&gspb.Edges_Target{
   107  			Node: &scpb.Node{
   108  				Source: &spb.VName{Signature: "child1"},
   109  				Kind:   &scpb.Node_KytheKind{scpb.NodeKind_FUNCTION},
   110  			},
   111  		}},
   112  	}, {
   113  		Source: src,
   114  		Entry: &gspb.Edges_Target_{&gspb.Edges_Target{
   115  			Node: &scpb.Node{
   116  				Source:  &spb.VName{Signature: "child2"},
   117  				Kind:    &scpb.Node_KytheKind{scpb.NodeKind_RECORD},
   118  				Subkind: &scpb.Node_KytheSubkind{scpb.Subkind_CLASS},
   119  			},
   120  		}},
   121  	}, {
   122  		Source: src,
   123  		Entry: &gspb.Edges_Target_{&gspb.Edges_Target{
   124  			Node: &scpb.Node{
   125  				Source:  &spb.VName{Signature: "param0"},
   126  				Kind:    &scpb.Node_KytheKind{scpb.NodeKind_VARIABLE},
   127  				Subkind: &scpb.Node_KytheSubkind{scpb.Subkind_LOCAL_PARAMETER},
   128  			},
   129  		}},
   130  	}}
   131  	for _, fd := range edgeEntries {
   132  		mustWriteEdges(t, w, fd)
   133  	}
   134  
   135  	if err := w.Close(); err != nil {
   136  		t.Fatal(err)
   137  	}
   138  
   139  	gs := NewService(ctx, db)
   140  	srcTicket := kytheuri.ToString(src)
   141  
   142  	t.Run("source_node", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{
   143  		Ticket: []string{srcTicket},
   144  		Kind:   []string{"non_existent_kind"},
   145  		Filter: []string{"**"},
   146  	}, &gpb.EdgesReply{
   147  		Nodes: map[string]*cpb.NodeInfo{
   148  			srcTicket: &cpb.NodeInfo{
   149  				Facts: map[string][]byte{
   150  					facts.NodeKind: []byte(kinds.Record),
   151  					facts.Text:     []byte("value"),
   152  				},
   153  			},
   154  		},
   155  	}))
   156  
   157  	t.Run("filter_node_kind", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{
   158  		Ticket: []string{srcTicket},
   159  		Kind:   []string{"non_existent_kind"},
   160  		Filter: []string{"/kythe/node/kind"},
   161  	}, &gpb.EdgesReply{
   162  		Nodes: map[string]*cpb.NodeInfo{
   163  			srcTicket: {
   164  				Facts: map[string][]byte{
   165  					facts.NodeKind: []byte(kinds.Record),
   166  				},
   167  			},
   168  		},
   169  	}))
   170  
   171  	t.Run("ordinals", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{
   172  		Ticket: []string{srcTicket},
   173  		Kind:   []string{edges.Param},
   174  	}, &gpb.EdgesReply{
   175  		EdgeSets: map[string]*gpb.EdgeSet{
   176  			srcTicket: {
   177  				Groups: map[string]*gpb.EdgeSet_Group{
   178  					edges.Param: {
   179  						Edge: []*gpb.EdgeSet_Group_Edge{{
   180  							Ordinal:      0,
   181  							TargetTicket: "kythe:#param0",
   182  						}, {
   183  							Ordinal:      1,
   184  							TargetTicket: "kythe:#param1",
   185  						}},
   186  					},
   187  				},
   188  			},
   189  		},
   190  	}))
   191  
   192  	t.Run("reverse", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{
   193  		Ticket: []string{srcTicket},
   194  		Kind:   []string{"%" + edges.ChildOf},
   195  	}, &gpb.EdgesReply{
   196  		EdgeSets: map[string]*gpb.EdgeSet{
   197  			srcTicket: {
   198  				Groups: map[string]*gpb.EdgeSet_Group{
   199  					"%" + edges.ChildOf: {
   200  						Edge: []*gpb.EdgeSet_Group_Edge{{
   201  							TargetTicket: "kythe:#child1",
   202  						}, {
   203  							TargetTicket: "kythe:#child2",
   204  						}},
   205  					},
   206  				},
   207  			},
   208  		},
   209  	}))
   210  
   211  	t.Run("filtered_targets", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{
   212  		Ticket: []string{srcTicket},
   213  		Kind:   []string{"%" + edges.ChildOf},
   214  		Filter: []string{"**"},
   215  	}, &gpb.EdgesReply{
   216  		EdgeSets: map[string]*gpb.EdgeSet{
   217  			srcTicket: {
   218  				Groups: map[string]*gpb.EdgeSet_Group{
   219  					"%" + edges.ChildOf: {
   220  						Edge: []*gpb.EdgeSet_Group_Edge{{
   221  							TargetTicket: "kythe:#child1",
   222  						}, {
   223  							TargetTicket: "kythe:#child2",
   224  						}},
   225  					},
   226  				},
   227  			},
   228  		},
   229  		Nodes: map[string]*cpb.NodeInfo{
   230  			srcTicket: &cpb.NodeInfo{
   231  				Facts: map[string][]byte{
   232  					facts.NodeKind: []byte(kinds.Record),
   233  					facts.Text:     []byte("value"),
   234  				},
   235  			},
   236  			"kythe:#child1": &cpb.NodeInfo{
   237  				Facts: map[string][]byte{
   238  					facts.NodeKind: []byte(kinds.Function),
   239  				},
   240  			},
   241  			"kythe:#child2": &cpb.NodeInfo{
   242  				Facts: map[string][]byte{
   243  					facts.NodeKind: []byte(kinds.Record),
   244  					facts.Subkind:  []byte(kinds.Class),
   245  				},
   246  			},
   247  		},
   248  	}))
   249  
   250  	t.Run("all", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{
   251  		Ticket: []string{srcTicket},
   252  		Filter: []string{facts.NodeKind},
   253  	}, &gpb.EdgesReply{
   254  		EdgeSets: map[string]*gpb.EdgeSet{
   255  			srcTicket: {
   256  				Groups: map[string]*gpb.EdgeSet_Group{
   257  					edges.Param: {
   258  						Edge: []*gpb.EdgeSet_Group_Edge{{
   259  							Ordinal:      0,
   260  							TargetTicket: "kythe:#param0",
   261  						}, {
   262  							Ordinal:      1,
   263  							TargetTicket: "kythe:#param1",
   264  						}},
   265  					},
   266  					"%" + edges.ChildOf: {
   267  						Edge: []*gpb.EdgeSet_Group_Edge{{
   268  							TargetTicket: "kythe:#child1",
   269  						}, {
   270  							TargetTicket: "kythe:#child2",
   271  						}},
   272  					},
   273  				},
   274  			},
   275  		},
   276  		Nodes: map[string]*cpb.NodeInfo{
   277  			srcTicket:       &cpb.NodeInfo{Facts: map[string][]byte{facts.NodeKind: []byte(kinds.Record)}},
   278  			"kythe:#child1": &cpb.NodeInfo{Facts: map[string][]byte{facts.NodeKind: []byte(kinds.Function)}},
   279  			"kythe:#child2": &cpb.NodeInfo{Facts: map[string][]byte{facts.NodeKind: []byte(kinds.Record)}},
   280  			"kythe:#param0": &cpb.NodeInfo{Facts: map[string][]byte{facts.NodeKind: []byte(kinds.Variable)}},
   281  		},
   282  	}))
   283  }
   284  
   285  func TestServingNodes(t *testing.T) {
   286  	ctx := context.Background()
   287  	db := inmemory.NewKeyValueDB()
   288  	w, err := db.Writer(ctx)
   289  	if err != nil {
   290  		t.Fatal(err)
   291  	}
   292  
   293  	// Mark table as columnar
   294  	mustWrite(t, w, []byte(ColumnarTableKeyMarker), []byte{})
   295  
   296  	edgeEntries := []*gspb.Edges{{
   297  		Source: &spb.VName{Signature: "node1"},
   298  		Entry: &gspb.Edges_Index_{&gspb.Edges_Index{
   299  			Node: &scpb.Node{
   300  				Kind: &scpb.Node_KytheKind{scpb.NodeKind_RECORD},
   301  				Fact: []*scpb.Fact{{
   302  					Name:  &scpb.Fact_KytheName{scpb.FactName_TEXT},
   303  					Value: []byte("value"),
   304  				}},
   305  			},
   306  		}},
   307  	}, {
   308  		Source: &spb.VName{Signature: "node2"},
   309  		Entry: &gspb.Edges_Index_{&gspb.Edges_Index{
   310  			Node: &scpb.Node{
   311  				Subkind: &scpb.Node_KytheSubkind{scpb.Subkind_CLASS},
   312  				Fact: []*scpb.Fact{{
   313  					Name:  &scpb.Fact_KytheName{scpb.FactName_TEXT},
   314  					Value: []byte("text"),
   315  				}},
   316  			},
   317  		}},
   318  	}, {
   319  		Source: &spb.VName{Signature: "node3"},
   320  		Entry: &gspb.Edges_Index_{&gspb.Edges_Index{
   321  			Node: &scpb.Node{
   322  				Kind:    &scpb.Node_KytheKind{scpb.NodeKind_VARIABLE},
   323  				Subkind: &scpb.Node_KytheSubkind{scpb.Subkind_LOCAL_PARAMETER},
   324  				Fact: []*scpb.Fact{{
   325  					Name:  &scpb.Fact_KytheName{scpb.FactName_TEXT},
   326  					Value: []byte("text3"),
   327  				}},
   328  			},
   329  		}},
   330  	}}
   331  	for _, fd := range edgeEntries {
   332  		mustWriteEdges(t, w, fd)
   333  	}
   334  
   335  	if err := w.Close(); err != nil {
   336  		t.Fatal(err)
   337  	}
   338  
   339  	gs := NewService(ctx, db)
   340  
   341  	t.Run("nodes", makeNodesTestCase(ctx, gs, &gpb.NodesRequest{
   342  		Ticket: []string{"kythe:#node1", "kythe:#node2", "kythe:#bad", "kythe:#node3"},
   343  	}, &gpb.NodesReply{
   344  		Nodes: map[string]*cpb.NodeInfo{
   345  			"kythe:#node1": &cpb.NodeInfo{
   346  				Facts: map[string][]byte{
   347  					facts.NodeKind: []byte(kinds.Record),
   348  					facts.Text:     []byte("value"),
   349  				},
   350  			},
   351  			"kythe:#node2": &cpb.NodeInfo{
   352  				Facts: map[string][]byte{
   353  					facts.Subkind: []byte(kinds.Class),
   354  					facts.Text:    []byte("text"),
   355  				},
   356  			},
   357  			"kythe:#node3": &cpb.NodeInfo{
   358  				Facts: map[string][]byte{
   359  					facts.NodeKind: []byte(kinds.Variable),
   360  					facts.Subkind:  []byte(kinds.LocalParameter),
   361  					facts.Text:     []byte("text3"),
   362  				},
   363  			},
   364  		},
   365  	}))
   366  
   367  	t.Run("single_node", makeNodesTestCase(ctx, gs, &gpb.NodesRequest{
   368  		Ticket: []string{"kythe:#node1"},
   369  	}, &gpb.NodesReply{
   370  		Nodes: map[string]*cpb.NodeInfo{
   371  			"kythe:#node1": &cpb.NodeInfo{
   372  				Facts: map[string][]byte{
   373  					facts.NodeKind: []byte(kinds.Record),
   374  					facts.Text:     []byte("value"),
   375  				},
   376  			},
   377  		},
   378  	}))
   379  
   380  	t.Run("filter", makeNodesTestCase(ctx, gs, &gpb.NodesRequest{
   381  		Ticket: []string{"kythe:#node1", "kythe:#node2", "kythe:#bad", "kythe:#node3"},
   382  		Filter: []string{facts.NodeKind},
   383  	}, &gpb.NodesReply{
   384  		Nodes: map[string]*cpb.NodeInfo{
   385  			"kythe:#node1": &cpb.NodeInfo{
   386  				Facts: map[string][]byte{
   387  					facts.NodeKind: []byte(kinds.Record),
   388  				},
   389  			},
   390  			"kythe:#node3": &cpb.NodeInfo{
   391  				Facts: map[string][]byte{
   392  					facts.NodeKind: []byte(kinds.Variable),
   393  				},
   394  			},
   395  		},
   396  	}))
   397  }
   398  
   399  func makeEdgesTestCase(ctx context.Context, gs graph.Service, req *gpb.EdgesRequest, expected *gpb.EdgesReply) func(*testing.T) {
   400  	return func(t *testing.T) {
   401  		reply, err := gs.Edges(ctx, req)
   402  		if err != nil {
   403  			t.Fatalf("Edges error: %v", err)
   404  		}
   405  		if diff := compare.ProtoDiff(expected, reply); diff != "" {
   406  			t.Fatalf("EdgesReply differences: (- expected; + found)\n%s", diff)
   407  		}
   408  	}
   409  }
   410  
   411  func makeNodesTestCase(ctx context.Context, gs graph.Service, req *gpb.NodesRequest, expected *gpb.NodesReply) func(*testing.T) {
   412  	return func(t *testing.T) {
   413  		reply, err := gs.Nodes(ctx, req)
   414  		if err != nil {
   415  			t.Fatalf("Nodes error: %v", err)
   416  		}
   417  		if diff := compare.ProtoDiff(expected, reply); diff != "" {
   418  			t.Fatalf("NodesReply differences: (- expected; + found)\n%s", diff)
   419  		}
   420  	}
   421  }