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.MakeRaw("C", "follows", "B", ""), quad.MakeRaw("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.MakeRaw("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 TestMultipleConstraintParse(t *testing.T) { qs, _ := graph.NewQuadStore("memstore", "", nil) w, _ := graph.NewQuadWriter("single", qs, nil) for _, tv := range []quad.Quad{ quad.MakeRaw("i", "like", "food", ""), quad.MakeRaw("i", "like", "beer", ""), quad.MakeRaw("you", "like", "beer", ""), } { w.AddQuad(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 !it.Next() { 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 it.Next() { t.Error("Too many results") } }
func TestRemoveQuad(t *testing.T) { qs, w, _ := makeTestStore(simpleGraph) err := w.RemoveQuad(quad.MakeRaw( "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 newIt.Next() { 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.MakeRaw("E", "follows", "F", ""), }) it.Reset() w.RemoveQuad(quad.MakeRaw("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.AddQuad(quad.MakeRaw("i", "like", "food", "")) w.AddQuad(quad.MakeRaw("food", "is", "good", "")) query := "(\"i\"\n" + "(:like\n" + "($a (:is :good))))" it := BuildIteratorTreeForQuery(qs, query) if !it.Next() { 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.AddQuad(quad.MakeRaw("i", "like", "food", "")) w.AddQuad(quad.MakeRaw("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 !it.Next() { 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.MakeRaw( "E", "follows", "G", "")) tx.RemoveQuad(quad.MakeRaw( "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.MakeRaw( "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.MakeRaw( "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") } }
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.MakeRaw("alice", "follows", "bob", ""), quad.MakeRaw("bob", "follows", "alice", ""), quad.MakeRaw("charlie", "follows", "bob", ""), quad.MakeRaw("dani", "follows", "charlie", ""), quad.MakeRaw("dani", "follows", "alice", ""), quad.MakeRaw("alice", "is", "cool", ""), quad.MakeRaw("bob", "is", "not cool", ""), quad.MakeRaw("charlie", "is", "cool", ""), quad.MakeRaw("danie", "is", "not cool", ""), } func TestIssue160(t *testing.T) { query := `g.V().Tag('query').Out(raw('follows')).Out(raw('follows')).ForEach(function (item) { if (item.id !== item.query) g.Emit({ id: item.id }); })` expect := []string{ "****\nid : alice\n", "****\nid : bob\n",
func TestTransaction(t *testing.T) { var tx *Transaction // simples adds / removes tx = NewTransaction() tx.AddQuad(quad.MakeRaw("E", "follows", "F", "")) tx.AddQuad(quad.MakeRaw("F", "follows", "G", "")) tx.RemoveQuad(quad.MakeRaw("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.MakeRaw("E", "follows", "G", "")) tx.RemoveQuad(quad.MakeRaw("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.MakeRaw("E", "follows", "G", "")) tx.AddQuad(quad.MakeRaw("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.MakeRaw("E", "follows", "G", "")) tx.AddQuad(quad.MakeRaw("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.MakeRaw("E", "follows", "G", "")) tx.RemoveQuad(quad.MakeRaw("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.MakeRaw("E", "follows", "G", "")) tx.RemoveQuad(quad.MakeRaw("E", "follows", "G", "")) tx.RemoveQuad(quad.MakeRaw("E", "follows", "G", "")) if len(tx.Deltas) != 1 { t.Errorf("Expected [add, remove, remove]->[remove], have %d delta(s)", len(tx.Deltas)) } }
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.MakeRaw("i", "can", "win", ""), query: "($a (:can \"win\"))", typ: graph.And, expect: "i", }, { message: "get a single quad linkage", add: quad.MakeRaw("i", "can", "win", ""), query: "(\"i\" (:can $a))", typ: graph.And, expect: "i", }, } func TestMemstoreBackedSexp(t *testing.T) { qs, _ := graph.NewQuadStore("memstore", "", nil)
// This is a simple test graph. // // +---+ +---+ // | A |------- ->| F |<-- // +---+ \------>+---+-/ +---+ \--+---+ // ------>|#B#| | | E | // +---+-------/ >+---+ | +---+ // | C | / v // +---+ -/ +---+ // ---- +---+/ |#G#| // \-->|#D#|------------->+---+ // +---+ // func MakeQuadSet() []quad.Quad { return []quad.Quad{ quad.MakeRaw("A", "follows", "B", ""), quad.MakeRaw("C", "follows", "B", ""), quad.MakeRaw("C", "follows", "D", ""), quad.MakeRaw("D", "follows", "B", ""), quad.MakeRaw("B", "follows", "F", ""), quad.MakeRaw("F", "follows", "G", ""), quad.MakeRaw("D", "follows", "G", ""), quad.MakeRaw("E", "follows", "F", ""), quad.MakeRaw("B", "status", "cool", "status_graph"), quad.MakeRaw("D", "status", "cool", "status_graph"), quad.MakeRaw("G", "status", "cool", "status_graph"), } }
// 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.MakeRaw("A", "follows", "B", ""), // duplicate quad.MakeRaw("F", "follows", "B", ""), quad.MakeRaw("C", "follows", "D", ""), // duplicate quad.MakeRaw("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.MakeRaw("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) }
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.MakeRaw("C", "follows", "B", ""), quad.MakeRaw("C", "follows", "D", ""), }) it.Reset() and := iterator.NewAnd(qs) and.AddSubIterator(qs.QuadsAllIterator()) and.AddSubIterator(it) expectIteratedQuads(and, []quad.Quad{ quad.MakeRaw("C", "follows", "B", ""), quad.MakeRaw("C", "follows", "D", ""), }) // Object iterator. it = qs.QuadIterator(quad.Object, qs.ValueOf(quad.Raw("F"))) expectIteratedQuads(it, []quad.Quad{ quad.MakeRaw("B", "follows", "F", ""), quad.MakeRaw("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.MakeRaw("B", "follows", "F", ""), }) // Predicate iterator. it = qs.QuadIterator(quad.Predicate, qs.ValueOf(quad.Raw("status"))) expectIteratedQuads(it, []quad.Quad{ quad.MakeRaw("B", "status", "cool", "status_graph"), quad.MakeRaw("D", "status", "cool", "status_graph"), quad.MakeRaw("G", "status", "cool", "status_graph"), }) // Label iterator. it = qs.QuadIterator(quad.Label, qs.ValueOf(quad.Raw("status_graph"))) expectIteratedQuads(it, []quad.Quad{ quad.MakeRaw("B", "status", "cool", "status_graph"), quad.MakeRaw("D", "status", "cool", "status_graph"), quad.MakeRaw("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.MakeRaw("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.MakeRaw("B", "status", "cool", "status_graph"), }) }
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.AddQuad(quad.MakeRaw( "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.AddQuadSet(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.MakeRaw( "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() }
// This is a simple test graph. // // +---+ +---+ // | A |------- ->| F |<-- // +---+ \------>+---+-/ +---+ \--+---+ // ------>|#B#| | | E | // +---+-------/ >+---+ | +---+ // | C | / v // +---+ -/ +---+ // ---- +---+/ |#G#| // \-->|#D#|------------->+---+ // +---+ // var simpleGraph = []quad.Quad{ quad.MakeRaw("A", "follows", "B", ""), quad.MakeRaw("C", "follows", "B", ""), quad.MakeRaw("C", "follows", "D", ""), quad.MakeRaw("D", "follows", "B", ""), quad.MakeRaw("B", "follows", "F", ""), quad.MakeRaw("F", "follows", "G", ""), quad.MakeRaw("D", "follows", "G", ""), quad.MakeRaw("E", "follows", "F", ""), quad.MakeRaw("B", "status", "cool", "status_graph"), quad.MakeRaw("D", "status", "cool", "status_graph"), quad.MakeRaw("G", "status", "cool", "status_graph"), } func makeTestSession(data []quad.Quad) *Session { qs, _ := graph.NewQuadStore("memstore", "", nil) w, _ := graph.NewQuadWriter("single", qs, nil)
// This is a simple test graph. // // +---+ +---+ // | A |------- ->| F |<-- // +---+ \------>+---+-/ +---+ \--+---+ // ------>|#B#| | | E | // +---+-------/ >+---+ | +---+ // | C | / v // +---+ -/ +---+ // ---- +---+/ |#G#| // \-->|#D#|------------->+---+ // +---+ // var simpleGraph = graphtest.MakeQuadSet() var simpleGraphUpdate = []quad.Quad{ quad.MakeRaw("A", "follows", "B", ""), quad.MakeRaw("F", "follows", "B", ""), quad.MakeRaw("C", "follows", "D", ""), quad.MakeRaw("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)