github.com/cayleygraph/cayley@v0.7.7/query/mql/mql_test.go (about)

     1  // Copyright 2014 The Cayley Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mql
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"github.com/cayleygraph/cayley/graph"
    25  	"github.com/cayleygraph/cayley/graph/graphtest/testutil"
    26  	_ "github.com/cayleygraph/cayley/graph/memstore"
    27  	"github.com/cayleygraph/cayley/query"
    28  	_ "github.com/cayleygraph/cayley/writer"
    29  	"github.com/cayleygraph/quad"
    30  )
    31  
    32  // This is a simple test graph.
    33  //
    34  //    +---+                        +---+
    35  //    | A |-------               ->| F |<--
    36  //    +---+       \------>+---+-/  +---+   \--+---+
    37  //                 ------>|#B#|      |        | E |
    38  //    +---+-------/      >+---+      |        +---+
    39  //    | C |             /            v
    40  //    +---+           -/           +---+
    41  //      ----    +---+/             |#G#|
    42  //          \-->|#D#|------------->+---+
    43  //              +---+
    44  //
    45  
    46  func makeTestSession(data []quad.Quad) *Session {
    47  	qs, _ := graph.NewQuadStore("memstore", "", nil)
    48  	w, _ := graph.NewQuadWriter("single", qs, nil)
    49  	for _, t := range data {
    50  		w.AddQuad(t)
    51  	}
    52  	return NewSession(qs)
    53  }
    54  
    55  var testQueries = []struct {
    56  	message string
    57  	query   string
    58  	tag     string
    59  	expect  string
    60  }{
    61  	{
    62  		message: "get all IDs in the database",
    63  		query:   `[{"id": null}]`,
    64  		expect: `
    65  			[
    66  				{"id": "<alice>"},
    67  				{"id": "<follows>"},
    68  				{"id": "<bob>"},
    69  				{"id": "<fred>"},
    70  				{"id": "<status>"},
    71  				{"id": "cool_person"},
    72  				{"id": "<dani>"},
    73  				{"id": "<charlie>"},
    74  				{"id": "<greg>"},
    75  				{"id": "<emily>"},
    76  				{"id": "<predicates>"},
    77  				{"id": "<are>"},
    78  				{"id": "smart_person"},
    79  				{"id": "<smart_graph>"}
    80  			]
    81  		`,
    82  	},
    83  	{
    84  		message: "get nodes by status",
    85  		query:   `[{"id": null, "<status>": "cool_person"}]`,
    86  		expect: `
    87  			[
    88  				{"id": "<bob>", "<status>": "cool_person"},
    89  				{"id": "<dani>", "<status>": "cool_person"},
    90  				{"id": "<greg>", "<status>": "cool_person"}
    91  			]
    92  		`,
    93  	},
    94  	{
    95  		message: "show correct null semantics",
    96  		query:   `[{"id": "cool_person", "status": null}]`,
    97  		expect: `
    98  			[
    99  				{"id": "cool_person", "status": null}
   100  			]
   101  		`,
   102  	},
   103  	{
   104  		message: "get correct follows list",
   105  		query:   `[{"id": "<charlie>", "<follows>": []}]`,
   106  		expect: `
   107  			[
   108  				{"id": "<charlie>", "<follows>": ["<bob>", "<dani>"]}
   109  			]
   110  		`,
   111  	},
   112  	{
   113  		message: "get correct reverse follows list",
   114  		query:   `[{"id": "<fred>", "!<follows>": []}]`,
   115  		expect: `
   116  			[
   117  				{"id": "<fred>", "!<follows>": ["<bob>", "<emily>"]}
   118  			]
   119  		`,
   120  	},
   121  	{
   122  		message: "get correct follows struct",
   123  		query:   `[{"id": null, "<follows>": {"id": null, "<status>": "cool_person"}}]`,
   124  		expect: `
   125  			[
   126  				{"id": "<alice>", "<follows>": {"id": "<bob>", "<status>": "cool_person"}},
   127  				{"id": "<dani>", "<follows>": {"id": "<greg>", "<status>": "cool_person"}},
   128  				{"id": "<charlie>", "<follows>": {"id": "<dani>", "<status>": "cool_person"}},
   129  				{"id": "<fred>", "<follows>": {"id": "<greg>", "<status>": "cool_person"}}
   130  			]
   131  		`,
   132  	},
   133  	{
   134  		message: "get correct reverse follows struct",
   135  		query:   `[{"id": null, "!<follows>": [{"id": null, "<status>" : "cool_person"}]}]`,
   136  		expect: `
   137  			[
   138  				{"id": "<fred>", "!<follows>": [{"id": "<bob>", "<status>": "cool_person"}]},
   139  				{"id": "<bob>", "!<follows>": [{"id": "<dani>", "<status>": "cool_person"}]},
   140  				{"id": "<greg>", "!<follows>": [{"id": "<dani>", "<status>": "cool_person"}]}
   141  			]
   142  		`,
   143  	},
   144  	{
   145  		message: "get correct co-follows",
   146  		query:   `[{"id": null, "@A:<follows>": "<bob>", "@B:<follows>": "<dani>"}]`,
   147  		expect: `
   148  			[
   149  				{"id": "<charlie>", "@A:<follows>": "<bob>", "@B:<follows>": "<dani>"}
   150  			]
   151  		`,
   152  	},
   153  	{
   154  		message: "get correct reverse co-follows",
   155  		query:   `[{"id": null, "!<follows>": {"id": "<charlie>"}, "@A:!<follows>": "<dani>"}]`,
   156  		expect: `
   157  			[
   158  				{"id": "<bob>", "!<follows>": {"id": "<charlie>"}, "@A:!<follows>": "<dani>"}
   159  			]
   160  		`,
   161  	},
   162  }
   163  
   164  func runQuery(t testing.TB, g []quad.Quad, qu string) interface{} {
   165  	s := makeTestSession(g)
   166  	ctx := context.TODO()
   167  	it, err := s.Execute(ctx, qu, query.Options{Collation: query.JSON})
   168  	if err != nil {
   169  		t.Fatal(err)
   170  	}
   171  	defer it.Close()
   172  	var out []interface{}
   173  	for it.Next(ctx) {
   174  		out = append(out, it.Result())
   175  	}
   176  	return out
   177  }
   178  
   179  func TestMQL(t *testing.T) {
   180  	simpleGraph := testutil.LoadGraph(t, "../../data/testdata.nq")
   181  	for _, test := range testQueries {
   182  		t.Run(test.message, func(t *testing.T) {
   183  			got := runQuery(t, simpleGraph, test.query)
   184  			var expect interface{}
   185  			json.Unmarshal([]byte(test.expect), &expect)
   186  			require.Equal(t, expect, got)
   187  		})
   188  	}
   189  }