func TestMultipleConstraintParse(t *testing.T) { qs, _ := graph.NewQuadStore("memstore", "", nil) w, _ := graph.NewQuadWriter("single", qs, nil) for _, tv := range []quad.Quad{ quad.Make("i", "like", "food", ""), quad.Make("i", "like", "beer", ""), quad.Make("you", "like", "beer", ""), } { w.WriteQuad(tv) } query := `( $a (:like :beer) (:like "food") )` it := BuildIteratorTreeForQuery(qs, query) if it.Type() != graph.And { t.Errorf("Odd iterator tree. Got: %#v", it.Describe()) } if !graph.Next(it) { t.Error("Got no results") } out := it.Result() if out != qs.ValueOf(quad.Raw("i")) { t.Errorf("Got %d, expected %d", out, qs.ValueOf(quad.Raw("i"))) } if graph.Next(it) { t.Error("Too many results") } }
func TestIterators(t *testing.T) { qs, opts, closer := makeGAE(t) defer closer() graphtest.MakeWriter(t, qs, opts, graphtest.MakeQuadSet()...) require.Equal(t, int64(11), qs.Size(), "Incorrect number of quads") var expected = []quad.Quad{ quad.Make("C", "follows", "B", ""), quad.Make("C", "follows", "D", ""), } it := qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw("C"))) graphtest.ExpectIteratedQuads(t, qs, it, expected) // Test contains it = qs.QuadIterator(quad.Label, qs.ValueOf(quad.Raw("status_graph"))) gqs := qs.(*QuadStore) key := gqs.createKeyForQuad(quad.Make("G", "status", "cool", "status_graph")) token := &Token{quadKind, key.StringID()} require.True(t, it.Contains(token), "Contains failed") // Test cloning an iterator var it2 graph.Iterator it2 = it.Clone() x := it2.Describe() y := it.Describe() require.Equal(t, y.Name, x.Name, "Iterator Clone was not successful") }
func TestRemoveQuad(t *testing.T) { qs, w, _ := makeTestStore(simpleGraph) err := w.RemoveQuad(quad.Make( "E", "follows", "F", "", )) if err != nil { t.Error("Couldn't remove quad", err) } fixed := qs.FixedIterator() fixed.Add(qs.ValueOf(quad.Raw("E"))) fixed2 := qs.FixedIterator() fixed2.Add(qs.ValueOf(quad.Raw("follows"))) innerAnd := iterator.NewAnd(qs) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, fixed, quad.Subject)) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, fixed2, quad.Predicate)) hasa := iterator.NewHasA(qs, innerAnd, quad.Object) newIt, _ := hasa.Optimize() if graph.Next(newIt) { t.Error("E should not have any followers.") } }
func TestDeletedFromIterator(t testing.TB, gen DatabaseFunc) { qs, opts, closer := gen(t) defer closer() w := MakeWriter(t, qs, opts, MakeQuadSet()...) // Subject iterator. it := qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw("E"))) ExpectIteratedQuads(t, qs, it, []quad.Quad{ quad.Make("E", "follows", "F", ""), }) it.Reset() w.RemoveQuad(quad.Make("E", "follows", "F", "")) ExpectIteratedQuads(t, qs, it, nil) }
func TestTreeConstraintTagParse(t *testing.T) { qs, _ := graph.NewQuadStore("memstore", "", nil) w, _ := graph.NewQuadWriter("single", qs, nil) w.WriteQuad(quad.Make("i", "like", "food", "")) w.WriteQuad(quad.Make("food", "is", "good", "")) query := "(\"i\"\n" + "(:like\n" + "($a (:is :good))))" it := BuildIteratorTreeForQuery(qs, query) if !graph.Next(it) { t.Error("Got no results") } tags := make(map[string]graph.Value) it.TagResults(tags) if qs.NameOf(tags["$a"]).String() != "food" { t.Errorf("Got %s, expected food", qs.NameOf(tags["$a"])) } }
func TestTreeConstraintParse(t *testing.T) { qs, _ := graph.NewQuadStore("memstore", "", nil) w, _ := graph.NewQuadWriter("single", qs, nil) w.WriteQuad(quad.Make("i", "like", "food", "")) w.WriteQuad(quad.Make("food", "is", "good", "")) query := "(\"i\"\n" + "(:like\n" + "($a (:is :good))))" it := BuildIteratorTreeForQuery(qs, query) if it.Type() != graph.And { t.Errorf("Odd iterator tree. Got: %#v", it.Describe()) } if !graph.Next(it) { t.Error("Got no results") } out := it.Result() if out != qs.ValueOf(quad.Raw("i")) { t.Errorf("Got %d, expected %d", out, qs.ValueOf(quad.Raw("i"))) } }
func TestTransaction(t *testing.T) { qs, w, _ := makeTestStore(simpleGraph) size := qs.Size() tx := graph.NewTransaction() tx.AddQuad(quad.Make( "E", "follows", "G", "")) tx.RemoveQuad(quad.Make( "Non", "existent", "quad", "")) err := w.ApplyTransaction(tx) if err == nil { t.Error("Able to remove a non-existent quad") } if size != qs.Size() { t.Error("Appended a new quad in a failed transaction") } }
func TestLoadOneQuad(t testing.TB, gen DatabaseFunc) { qs, opts, closer := gen(t) defer closer() w := MakeWriter(t, qs, opts) err := w.AddQuad(quad.Make( "Something", "points_to", "Something Else", "context", )) require.Nil(t, err) for _, pq := range []string{"Something", "points_to", "Something Else", "context"} { got := quad.StringOf(qs.NameOf(qs.ValueOf(quad.Raw(pq)))) require.Equal(t, pq, got, "Failed to roundtrip %q", pq) } require.Equal(t, int64(1), qs.Size(), "Unexpected quadstore size") }
func TestHorizonInt(t testing.TB, gen DatabaseFunc, conf *Config) { qs, opts, closer := gen(t) defer closer() w := MakeWriter(t, qs, opts) horizon := qs.Horizon() require.Equal(t, int64(0), horizon.Int(), "Unexpected horizon value") err := w.AddQuadSet(MakeQuadSet()) require.Nil(t, err) require.Equal(t, int64(11), qs.Size(), "Unexpected quadstore size") if qss, ok := qs.(ValueSizer); ok { s := qss.SizeOf(qs.ValueOf(quad.Raw("B"))) require.Equal(t, int64(5), s, "Unexpected quadstore value size") } horizon = qs.Horizon() require.Equal(t, int64(11), horizon.Int(), "Unexpected horizon value") err = w.RemoveQuad(quad.Make( "A", "follows", "B", "", )) require.Nil(t, err) if !conf.SkipSizeCheckAfterDelete { require.Equal(t, int64(10), qs.Size(), "Unexpected quadstore size after RemoveQuad") } else { require.Equal(t, int64(11), qs.Size(), "Unexpected quadstore size") } if qss, ok := qs.(ValueSizer); ok { s := qss.SizeOf(qs.ValueOf(quad.Raw("B"))) require.Equal(t, int64(4), s, "Unexpected quadstore value size") } }
func Quad(subject, predicate, object, label string) quad.Quad { return quad.Make(subject, predicate, object, label) }
) var readTests = []struct { message string input string expect []quad.Quad err error }{ { message: "parse correct JSON", input: `[ {"subject": "foo", "predicate": "bar", "object": "baz"}, {"subject": "foo", "predicate": "bar", "object": "baz", "label": "graph"} ]`, expect: []quad.Quad{ quad.Make("foo", "bar", "baz", ""), quad.Make("foo", "bar", "baz", "graph"), }, err: nil, }, { message: "parse correct JSON with extra field", input: `[ {"subject": "foo", "predicate": "bar", "object": "foo", "something_else": "extra data"} ]`, expect: []quad.Quad{ quad.Make("foo", "bar", "foo", ""), }, err: nil, }, {
func TestSetIterator(t testing.TB, gen DatabaseFunc) { qs, opts, closer := gen(t) defer closer() MakeWriter(t, qs, opts, MakeQuadSet()...) expectIteratedQuads := func(it graph.Iterator, exp []quad.Quad) { ExpectIteratedQuads(t, qs, it, exp) } // Subject iterator. it := qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw("C"))) expectIteratedQuads(it, []quad.Quad{ quad.Make("C", "follows", "B", ""), quad.Make("C", "follows", "D", ""), }) it.Reset() and := iterator.NewAnd(qs) and.AddSubIterator(qs.QuadsAllIterator()) and.AddSubIterator(it) expectIteratedQuads(and, []quad.Quad{ quad.Make("C", "follows", "B", ""), quad.Make("C", "follows", "D", ""), }) // Object iterator. it = qs.QuadIterator(quad.Object, qs.ValueOf(quad.Raw("F"))) expectIteratedQuads(it, []quad.Quad{ quad.Make("B", "follows", "F", ""), quad.Make("E", "follows", "F", ""), }) and = iterator.NewAnd(qs) and.AddSubIterator(qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw("B")))) and.AddSubIterator(it) expectIteratedQuads(and, []quad.Quad{ quad.Make("B", "follows", "F", ""), }) // Predicate iterator. it = qs.QuadIterator(quad.Predicate, qs.ValueOf(quad.Raw("status"))) expectIteratedQuads(it, []quad.Quad{ quad.Make("B", "status", "cool", "status_graph"), quad.Make("D", "status", "cool", "status_graph"), quad.Make("G", "status", "cool", "status_graph"), }) // Label iterator. it = qs.QuadIterator(quad.Label, qs.ValueOf(quad.Raw("status_graph"))) expectIteratedQuads(it, []quad.Quad{ quad.Make("B", "status", "cool", "status_graph"), quad.Make("D", "status", "cool", "status_graph"), quad.Make("G", "status", "cool", "status_graph"), }) it.Reset() // Order is important and = iterator.NewAnd(qs) and.AddSubIterator(qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw("B")))) and.AddSubIterator(it) expectIteratedQuads(and, []quad.Quad{ quad.Make("B", "status", "cool", "status_graph"), }) it.Reset() // Order is important and = iterator.NewAnd(qs) and.AddSubIterator(it) and.AddSubIterator(qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw("B")))) expectIteratedQuads(and, []quad.Quad{ quad.Make("B", "status", "cool", "status_graph"), }) }
// This is a simple test graph. // // +---+ +---+ // | A |------- ->| F |<-- // +---+ \------>+---+-/ +---+ \--+---+ // ------>|#B#| | | E | // +---+-------/ >+---+ | +---+ // | C | / v // +---+ -/ +---+ // ---- +---+/ |#G#| // \-->|#D#|------------->+---+ // +---+ // var simpleGraph = graphtest.MakeQuadSet() var simpleGraphUpdate = []quad.Quad{ quad.Make("A", "follows", "B", ""), quad.Make("F", "follows", "B", ""), quad.Make("C", "follows", "D", ""), quad.Make("X", "follows", "B", ""), } type pair struct { query string value int64 } func makeTestStore(data []quad.Quad, opts graph.Options) (graph.QuadStore, graph.QuadWriter, []pair) { seen := make(map[quad.Value]struct{}) qs, _ := newQuadStore("", opts) qs, _ = newQuadStoreForRequest(qs, opts)
t.Errorf("%+v (%T %T %T %T)", q, q.Subject, q.Predicate, q.Object, q.Label) } t.Errorf("case %d failed: wrong quads returned:\n%v\n%v", i, quads, c.expect) } r.Close() } } var testWriteCases = []struct { data []quad.Quad ctx interface{} expect string }{ { []quad.Quad{ quad.Make(`<http://example.org/id1>`, `<http://example.org/term1>`, `"v1"^^<http://example.org/datatype>`, ``), quad.Make(`<http://example.org/id1>`, `<http://example.org/term2>`, `<http://example.org/id2>`, ``), quad.Make(`<http://example.org/id1>`, `<http://example.org/term3>`, `"v3"@en`, ``), quad.Make(`<http://example.org/id1>`, `<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>`, `<http://example.org/Type1>`, ``), quad.Make(`<http://example.org/id1>`, `<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>`, `<http://example.org/Type2>`, ``), }, map[string]interface{}{ "ex": "http://example.org/", }, `{ "@context": { "ex": "http://example.org/" }, "@id": "ex:id1", "@type": [ "ex:Type1",
func TestTransaction(t *testing.T) { var tx *Transaction // simples adds / removes tx = NewTransaction() tx.AddQuad(quad.Make("E", "follows", "F", "")) tx.AddQuad(quad.Make("F", "follows", "G", "")) tx.RemoveQuad(quad.Make("A", "follows", "Z", "")) if len(tx.Deltas) != 3 { t.Errorf("Expected 3 Deltas, have %d delta(s)", len(tx.Deltas)) } // add, remove -> nothing tx = NewTransaction() tx.AddQuad(quad.Make("E", "follows", "G", "")) tx.RemoveQuad(quad.Make("E", "follows", "G", "")) if len(tx.Deltas) != 0 { t.Errorf("Expected [add, remove]->[], have %d Deltas", len(tx.Deltas)) } // remove, add -> nothing tx = NewTransaction() tx.RemoveQuad(quad.Make("E", "follows", "G", "")) tx.AddQuad(quad.Make("E", "follows", "G", "")) if len(tx.Deltas) != 0 { t.Errorf("Expected [add, remove]->[], have %d delta(s)", len(tx.Deltas)) } // add x2 -> add x1 tx = NewTransaction() tx.AddQuad(quad.Make("E", "follows", "G", "")) tx.AddQuad(quad.Make("E", "follows", "G", "")) if len(tx.Deltas) != 1 { t.Errorf("Expected [add, add]->[add], have %d delta(s)", len(tx.Deltas)) } // remove x2 -> remove x1 tx = NewTransaction() tx.RemoveQuad(quad.Make("E", "follows", "G", "")) tx.RemoveQuad(quad.Make("E", "follows", "G", "")) if len(tx.Deltas) != 1 { t.Errorf("Expected [remove, remove]->[remove], have %d delta(s)", len(tx.Deltas)) } // add, remove x2 -> remove x1 tx = NewTransaction() tx.AddQuad(quad.Make("E", "follows", "G", "")) tx.RemoveQuad(quad.Make("E", "follows", "G", "")) tx.RemoveQuad(quad.Make("E", "follows", "G", "")) if len(tx.Deltas) != 1 { t.Errorf("Expected [add, remove, remove]->[remove], have %d delta(s)", len(tx.Deltas)) } }
func TestLoadDatabase(t *testing.T) { tmpFile, err := ioutil.TempFile(os.TempDir(), "cayley_test") if err != nil { t.Fatalf("Could not create working directory: %v", err) } defer os.RemoveAll(tmpFile.Name()) t.Log(tmpFile.Name()) err = createNewBolt(tmpFile.Name(), nil) if err != nil { t.Fatal("Failed to create Bolt database.", err) } qs, err := newQuadStore(tmpFile.Name(), nil) if qs == nil || err != nil { t.Error("Failed to create Bolt QuadStore.") } w, _ := writer.NewSingleReplication(qs, nil) w.WriteQuad(quad.Make( "Something", "points_to", "Something Else", "context", )) for _, pq := range []string{"Something", "points_to", "Something Else", "context"} { if got := qs.NameOf(qs.ValueOf(quad.Raw(pq))).String(); got != pq { t.Errorf("Failed to roundtrip %q, got:%q expect:%q", pq, got, pq) } } if s := qs.Size(); s != 1 { t.Errorf("Unexpected quadstore size, got:%d expect:1", s) } qs.Close() err = createNewBolt(tmpFile.Name(), nil) if err != graph.ErrDatabaseExists { t.Fatal("Failed to create Bolt database.", err) } qs, err = newQuadStore(tmpFile.Name(), nil) if qs == nil || err != nil { t.Error("Failed to create Bolt QuadStore.") } w, _ = writer.NewSingleReplication(qs, nil) ts2, didConvert := qs.(*QuadStore) if !didConvert { t.Errorf("Could not convert from generic to Bolt QuadStore") } //Test horizon horizon := qs.Horizon() if horizon.Int() != 1 { t.Errorf("Unexpected horizon value, got:%d expect:1", horizon.Int()) } w.WriteQuads(graphtest.MakeQuadSet()) if s := qs.Size(); s != 12 { t.Errorf("Unexpected quadstore size, got:%d expect:12", s) } if s := ts2.SizeOf(qs.ValueOf(quad.Raw("B"))); s != 5 { t.Errorf("Unexpected quadstore size, got:%d expect:5", s) } horizon = qs.Horizon() if horizon.Int() != 12 { t.Errorf("Unexpected horizon value, got:%d expect:12", horizon.Int()) } w.RemoveQuad(quad.Make( "A", "follows", "B", "", )) if s := qs.Size(); s != 11 { t.Errorf("Unexpected quadstore size after RemoveQuad, got:%d expect:11", s) } if s := ts2.SizeOf(qs.ValueOf(quad.Raw("B"))); s != 4 { t.Errorf("Unexpected quadstore size, got:%d expect:4", s) } qs.Close() }
// TODO(dennwc): add tests to verify that QS behaves in a right way with IgnoreOptions, // returns ErrQuadExists, ErrQuadNotExists is doing rollback. func TestAddRemove(t testing.TB, gen DatabaseFunc, conf *Config) { qs, opts, closer := gen(t) defer closer() if opts == nil { opts = make(graph.Options) } opts["ignore_duplicate"] = true w := MakeWriter(t, qs, opts, MakeQuadSet()...) require.Equal(t, int64(11), qs.Size(), "Incorrect number of quads") all := qs.NodesAllIterator() expect := []string{ "A", "B", "C", "D", "E", "F", "G", "cool", "follows", "status", "status_graph", } ExpectIteratedRawStrings(t, qs, all, expect) // Add more quads, some conflicts err := w.AddQuadSet([]quad.Quad{ quad.Make("A", "follows", "B", ""), // duplicate quad.Make("F", "follows", "B", ""), quad.Make("C", "follows", "D", ""), // duplicate quad.Make("X", "follows", "B", ""), }) assert.Nil(t, err, "AddQuadSet failed") assert.Equal(t, int64(13), qs.Size(), "Incorrect number of quads") all = qs.NodesAllIterator() expect = []string{ "A", "B", "C", "D", "E", "F", "G", "X", "cool", "follows", "status", "status_graph", } ExpectIteratedRawStrings(t, qs, all, expect) // Remove quad toRemove := quad.Make("X", "follows", "B", "") err = w.RemoveQuad(toRemove) require.Nil(t, err, "RemoveQuad failed") if !conf.SkipNodeDelAfterQuadDel { expect = []string{ "A", "B", "C", "D", "E", "F", "G", "cool", "follows", "status", "status_graph", } } else { expect = []string{ "A", "B", "C", "D", "E", "F", "G", "X", "cool", "follows", "status", "status_graph", } } ExpectIteratedRawStrings(t, qs, all, nil) all = qs.NodesAllIterator() ExpectIteratedRawStrings(t, qs, all, expect) }
// This is a simple test graph. // // +---+ +---+ // | A |------- ->| F |<-- // +---+ \------>+---+-/ +---+ \--+---+ // ------>|#B#| | | E | // +---+-------/ >+---+ | +---+ // | C | / v // +---+ -/ +---+ // ---- +---+/ |#G#| // \-->|#D#|------------->+---+ // +---+ // func MakeQuadSet() []quad.Quad { return []quad.Quad{ quad.Make("A", "follows", "B", ""), quad.Make("C", "follows", "B", ""), quad.Make("C", "follows", "D", ""), quad.Make("D", "follows", "B", ""), quad.Make("B", "follows", "F", ""), quad.Make("F", "follows", "G", ""), quad.Make("D", "follows", "G", ""), quad.Make("E", "follows", "F", ""), quad.Make("B", "status", "cool", "status_graph"), quad.Make("D", "status", "cool", "status_graph"), quad.Make("G", "status", "cool", "status_graph"), } }
str := ParseString("()") if str != "" { t.Errorf("Unexpected parse result, got:%q", str) } } var testQueries = []struct { message string add quad.Quad query string typ graph.Type expect string }{ { message: "get a single quad linkage", add: quad.Make("i", "can", "win", ""), query: "($a (:can \"win\"))", typ: graph.And, expect: "i", }, { message: "get a single quad linkage", add: quad.Make("i", "can", "win", ""), query: "(\"i\" (:can $a))", typ: graph.And, expect: "i", }, } func TestMemstoreBackedSexp(t *testing.T) { qs, _ := graph.NewQuadStore("memstore", "", nil)
"bytes" "testing" "github.com/google/cayley/quad" "github.com/google/cayley/quad/gml" ) var testData = []struct { quads []quad.Quad data string }{ { []quad.Quad{ quad.Make( "_:subject1", "</film/performance/character>", `"Tomas de Torquemada"`, "", ), quad.Make( "_:subject1", "<http://an.example/predicate1>", `"object1"`, "", ), quad.Make( "<http://example.org/bob#me>", "<http://schema.org/birthDate>", `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`, "", ), },
// This is a simple test graph. // // +---+ +---+ // | A |------- ->| F |<-- // +---+ \------>+---+-/ +---+ \--+---+ // ------>|#B#| | | E | // +---+-------/ >+---+ | +---+ // | C | / v // +---+ -/ +---+ // ---- +---+/ |#G#| // \-->|#D#|------------->+---+ // +---+ // var simpleGraph = []quad.Quad{ quad.Make("A", "follows", "B", ""), quad.Make("C", "follows", "B", ""), quad.Make("C", "follows", "D", ""), quad.Make("D", "follows", "B", ""), quad.Make("B", "follows", "F", ""), quad.Make("F", "follows", "G", ""), quad.Make("D", "follows", "G", ""), quad.Make("E", "follows", "F", ""), quad.Make("B", "status", "cool", "status_graph"), quad.Make("D", "status", "cool", "status_graph"), quad.Make("G", "status", "cool", "status_graph"), } func makeTestSession(data []quad.Quad) *Session { qs, _ := graph.NewQuadStore("memstore", "", nil) w, _ := graph.NewQuadWriter("single", qs, nil)
if test.tag == "" { test.tag = TopResultTag } got := runQueryGetTag(rec, simpleGraph, test.query, test.tag) sort.Strings(got) sort.Strings(test.expect) t.Log("testing", test.message) if !reflect.DeepEqual(got, test.expect) { t.Errorf("Failed to %s, got: %v expected: %v", test.message, got, test.expect) } }() } } var issue160TestGraph = []quad.Quad{ quad.Make("alice", "follows", "bob", ""), quad.Make("bob", "follows", "alice", ""), quad.Make("charlie", "follows", "bob", ""), quad.Make("dani", "follows", "charlie", ""), quad.Make("dani", "follows", "alice", ""), quad.Make("alice", "is", "cool", ""), quad.Make("bob", "is", "not cool", ""), quad.Make("charlie", "is", "cool", ""), quad.Make("danie", "is", "not cool", ""), } func TestIssue160(t *testing.T) { query := `g.V().Tag('query').Out('follows').Out('follows').ForEach(function (item) { if (item.id !== item.query) g.Emit({ id: item.id }); })` expect := []string{ "****\nid : alice\n", "****\nid : bob\n",