mirror of
https://github.com/hanzoai/graph.git
synced 2026-10-30 18:35:37 +00:00
Derived from Dgraph (github.com/dgraph-io/dgraph) at commit 64804bd2, Apache-2.0. See NOTICE. WHAT THIS IS. The graph engine as a Go library: posting lists, the DQL parser, the type and tokenizer system, the index and schema layers, and the wire types. 39,170 lines of the upstream's 166,337. THE STORE is github.com/luxfi/zapdb, a Badger fork that kept the package name, the subpackages (options, pb, y) and the API — Open, OpenManaged, DB, Options — so the 74 import sites are aliased rather than rewritten. THE CODEC is ZAP. zapdb serializes with ZAP, not protobuf: its pb package builds the ZAP types by default and the protobuf ones only under `-tags grpc`, a path that does not compile (table/builder.go calls pb.Marshal, which that tag removes). One call site in posting needed converting; every other type here is Hanzo's own protobuf and is untouched. NO TRANSPORT. An embedded engine dials nothing, so nothing here imports google.golang.org/grpc. Removed: 20 transport functions in x, the generated service stubs for Raft, Zero and Worker (1,657 lines), and the JWT helper — identity belongs to IAM. The core called none of them. NOT KEPT: worker, conn, raftwal, the cluster commands, the GraphQL layer and the test harnesses. `query`, the DQL execution engine, went with them because it depends on worker; parsing DQL is available here, executing it is not. The history of the upstream project lives at github.com/dgraph-io/dgraph. This repository begins at the derivation because three quarters of that history describes code this fork does not carry.
692 lines
16 KiB
Go
692 lines
16 KiB
Go
/*
|
|
* SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package schema
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
badger "github.com/luxfi/zapdb"
|
|
"github.com/hanzoai/graph/protos/pb"
|
|
"github.com/hanzoai/graph/types"
|
|
"github.com/hanzoai/graph/x"
|
|
)
|
|
|
|
type nameType struct {
|
|
name string
|
|
typ *pb.SchemaUpdate
|
|
}
|
|
|
|
func checkSchema(t *testing.T, h map[string]*pb.SchemaUpdate, expected []nameType) {
|
|
if len(h) != len(expected) {
|
|
t.Errorf("In checkSchema, expected len(h) == len(expected)")
|
|
}
|
|
require.Len(t, h, len(expected))
|
|
for _, nt := range expected {
|
|
typ, found := h[nt.name]
|
|
require.True(t, found, nt)
|
|
require.EqualValuesf(t, *nt.typ, *typ, "found in map: %+v\n expected: %+v", *typ, *nt.typ)
|
|
}
|
|
}
|
|
|
|
var schemaVal = `
|
|
age:int .
|
|
|
|
name: string .
|
|
address: string .
|
|
<http://scalar.com/helloworld/> : string .
|
|
amount: bigfloat .
|
|
coordinates: float32vector .
|
|
indexvector: float32vector @index(hnsw(metric:"euclidean")) .
|
|
`
|
|
|
|
func TestSchema(t *testing.T) {
|
|
require.NoError(t, ParseBytes([]byte(schemaVal), 1))
|
|
checkSchema(t, State().predicate, []nameType{
|
|
{x.AttrInRootNamespace("name"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
ValueType: pb.Posting_STRING,
|
|
}},
|
|
{x.AttrInRootNamespace("address"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("address"),
|
|
ValueType: pb.Posting_STRING,
|
|
}},
|
|
{x.AttrInRootNamespace("http://scalar.com/helloworld/"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("http://scalar.com/helloworld/"),
|
|
ValueType: pb.Posting_STRING,
|
|
}},
|
|
{x.AttrInRootNamespace("age"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("age"),
|
|
ValueType: pb.Posting_INT,
|
|
}},
|
|
{x.AttrInRootNamespace("amount"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("amount"),
|
|
ValueType: pb.Posting_BIGFLOAT,
|
|
}},
|
|
{x.AttrInRootNamespace("coordinates"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("coordinates"),
|
|
ValueType: pb.Posting_VFLOAT,
|
|
}},
|
|
{x.AttrInRootNamespace("indexvector"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("indexvector"),
|
|
ValueType: pb.Posting_VFLOAT,
|
|
Tokenizer: []string{},
|
|
Directive: pb.SchemaUpdate_INDEX,
|
|
IndexSpecs: []*pb.VectorIndexSpec{
|
|
{
|
|
Name: "hnsw",
|
|
Options: []*pb.OptionPair{
|
|
{
|
|
Key: "metric",
|
|
Value: "euclidean",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}},
|
|
})
|
|
|
|
typ, err := State().TypeOf(x.AttrInRootNamespace("age"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, types.IntID, typ)
|
|
|
|
typ, err = State().TypeOf(x.AttrInRootNamespace("amount"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, types.BigFloatID, typ)
|
|
|
|
typ, err = State().TypeOf(x.AttrInRootNamespace("coordinates"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, types.VFloatID, typ)
|
|
|
|
_, err = State().TypeOf(x.AttrInRootNamespace("agea"))
|
|
require.Error(t, err)
|
|
}
|
|
|
|
var schemaVal1 = `
|
|
age:int .
|
|
|
|
name: string .
|
|
address: string .
|
|
|
|
)
|
|
`
|
|
|
|
func TestSchema1_Error(t *testing.T) {
|
|
require.Error(t, ParseBytes([]byte(schemaVal1), 1))
|
|
}
|
|
|
|
var schemaVal2 = `
|
|
name: ( string
|
|
`
|
|
|
|
func TestSchema2_Error(t *testing.T) {
|
|
require.Error(t, ParseBytes([]byte(schemaVal2), 1))
|
|
}
|
|
|
|
var schemaVal3 = `
|
|
test test: int
|
|
`
|
|
|
|
func TestSchema3_Error(t *testing.T) {
|
|
require.Error(t, ParseBytes([]byte(schemaVal3), 1))
|
|
}
|
|
|
|
var schemaIndexVal2 = `
|
|
name: string @index(exact, exact) .
|
|
address: string @index(term) .
|
|
id: id @index(exact, term, exact) .
|
|
`
|
|
|
|
// Duplicate tokenizers
|
|
func TestSchemaIndex_Error1(t *testing.T) {
|
|
require.Error(t, ParseBytes([]byte(schemaIndexVal2), 1))
|
|
}
|
|
|
|
var schemaIndexVal3Uid = `
|
|
person: uid @index .
|
|
`
|
|
|
|
var schemaIndexVal3Default = `
|
|
value: default @index .
|
|
`
|
|
|
|
var schemaIndexVal3Password = `
|
|
pass: password @index .
|
|
`
|
|
|
|
// Object types cant be indexed.
|
|
func TestSchemaIndex_Error2(t *testing.T) {
|
|
require.Error(t, ParseBytes([]byte(schemaIndexVal3Uid), 1))
|
|
require.Error(t, ParseBytes([]byte(schemaIndexVal3Default), 1))
|
|
require.Error(t, ParseBytes([]byte(schemaIndexVal3Password), 1))
|
|
}
|
|
|
|
var schemaIndexVal4 = `
|
|
name:string @index(exact term) .
|
|
`
|
|
|
|
// Missing comma.
|
|
func TestSchemaIndex_Error3(t *testing.T) {
|
|
require.Error(t, ParseBytes([]byte(schemaIndexVal4), 1))
|
|
}
|
|
|
|
var schemaIndexVal5 = `
|
|
age : int @index(int) .
|
|
name : string @index(exact) @count .
|
|
address : string @index(term) .
|
|
friend : [uid] @reverse @count .
|
|
`
|
|
|
|
func TestSchemaIndexCustom(t *testing.T) {
|
|
require.NoError(t, ParseBytes([]byte(schemaIndexVal5), 1))
|
|
checkSchema(t, State().predicate, []nameType{
|
|
{x.AttrInRootNamespace("name"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
ValueType: pb.Posting_STRING,
|
|
Tokenizer: []string{"exact"},
|
|
Directive: pb.SchemaUpdate_INDEX,
|
|
Count: true,
|
|
}},
|
|
{x.AttrInRootNamespace("address"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("address"),
|
|
ValueType: pb.Posting_STRING,
|
|
Tokenizer: []string{"term"},
|
|
Directive: pb.SchemaUpdate_INDEX,
|
|
}},
|
|
{x.AttrInRootNamespace("age"), &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("age"),
|
|
ValueType: pb.Posting_INT,
|
|
Tokenizer: []string{"int"},
|
|
Directive: pb.SchemaUpdate_INDEX,
|
|
}},
|
|
{x.AttrInRootNamespace("friend"), &pb.SchemaUpdate{
|
|
ValueType: pb.Posting_UID,
|
|
Predicate: x.AttrInRootNamespace("friend"),
|
|
Directive: pb.SchemaUpdate_REVERSE,
|
|
Count: true,
|
|
List: true,
|
|
}},
|
|
})
|
|
require.True(t, State().IsIndexed(context.Background(), x.AttrInRootNamespace("name")))
|
|
require.False(t, State().IsReversed(context.Background(), x.AttrInRootNamespace("name")))
|
|
require.Equal(t, "int", State().Tokenizer(context.Background(), x.AttrInRootNamespace("age"))[0].Name())
|
|
}
|
|
|
|
func TestParse(t *testing.T) {
|
|
reset()
|
|
_, err := Parse("age:int @index . name:string")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestParse2(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("")
|
|
require.NoError(t, err)
|
|
require.Nil(t, result.Preds)
|
|
}
|
|
|
|
func TestParse3_Error(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("age:uid @index .")
|
|
require.Error(t, err)
|
|
require.Nil(t, result)
|
|
}
|
|
|
|
func TestParse4_Error(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("alive:bool @index(geo) .")
|
|
require.Contains(t, err.Error(),
|
|
"Tokenizer: geo isn't valid for predicate: alive of type: bool")
|
|
require.Nil(t, result)
|
|
}
|
|
|
|
func TestParse4_NoError(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("name:string @index(fulltext) .")
|
|
require.NotNil(t, result)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestParse5_Error(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("value:default @index .")
|
|
require.Error(t, err)
|
|
require.Nil(t, result)
|
|
}
|
|
|
|
func TestParse6_Error(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("pass:password @index .")
|
|
require.Error(t, err)
|
|
require.Nil(t, result)
|
|
}
|
|
|
|
func TestParse7_Error(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("name:string @index .")
|
|
require.Error(t, err)
|
|
require.Nil(t, result)
|
|
}
|
|
|
|
func TestParse8_Error(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("dob:dateTime @index .")
|
|
require.Error(t, err)
|
|
require.Nil(t, result)
|
|
}
|
|
|
|
func TestParse9_Error(t *testing.T) {
|
|
reset()
|
|
result, err := Parse("age:uid @noconflict .")
|
|
require.NotNil(t, result)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestParseScalarList(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
jobs: [string] @index(term) .
|
|
occupations: [string] .
|
|
graduation: [dateTime] .
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(result.Preds))
|
|
require.EqualValues(t, &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("jobs"),
|
|
ValueType: 9,
|
|
Directive: pb.SchemaUpdate_INDEX,
|
|
Tokenizer: []string{"term"},
|
|
List: true,
|
|
}, result.Preds[0])
|
|
|
|
require.EqualValues(t, &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("occupations"),
|
|
ValueType: 9,
|
|
List: true,
|
|
}, result.Preds[1])
|
|
|
|
require.EqualValues(t, &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("graduation"),
|
|
ValueType: 5,
|
|
List: true,
|
|
}, result.Preds[2])
|
|
}
|
|
|
|
func TestParseScalarListError1(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
friend: [string .
|
|
`)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Unclosed [ while parsing schema for: friend")
|
|
require.Nil(t, result)
|
|
}
|
|
|
|
func TestParseScalarListError2(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
friend: string] .
|
|
`)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Invalid ending")
|
|
require.Nil(t, result)
|
|
}
|
|
|
|
func TestParseScalarListError3(t *testing.T) {
|
|
reset()
|
|
_, err := Parse(`
|
|
friend: [bool] .
|
|
`)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Unsupported type for list: [bool]")
|
|
}
|
|
|
|
func TestParseUidList(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
friend: [uid] .
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Preds))
|
|
require.EqualValues(t, &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("friend"),
|
|
ValueType: 7,
|
|
List: true,
|
|
}, result.Preds[0])
|
|
}
|
|
|
|
func TestParseUidSingleValue(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
friend: uid .
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Preds))
|
|
require.EqualValues(t, &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("friend"),
|
|
ValueType: 7,
|
|
List: false,
|
|
}, result.Preds[0])
|
|
}
|
|
func TestParseUnderscore(t *testing.T) {
|
|
reset()
|
|
_, err := Parse("_share_:string @index(term) .")
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestParseUpsert(t *testing.T) {
|
|
reset()
|
|
_, err := Parse(`
|
|
jobs : string @index(exact) @upsert .
|
|
age : int @index(int) @upsert .
|
|
`)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestParseEmptyType(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
type Person {
|
|
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Types))
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.AttrInRootNamespace("Person"),
|
|
}, result.Types[0])
|
|
|
|
}
|
|
|
|
func TestParseTypeEOF(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
type Person {
|
|
}`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Types))
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.AttrInRootNamespace("Person"),
|
|
}, result.Types[0])
|
|
|
|
}
|
|
|
|
func TestParseSingleType(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
type Person {
|
|
name
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Types))
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.AttrInRootNamespace("Person"),
|
|
Fields: []*pb.SchemaUpdate{
|
|
{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
},
|
|
},
|
|
}, result.Types[0])
|
|
}
|
|
|
|
func TestParseCombinedSchemasAndTypes(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
type Person {
|
|
name
|
|
}
|
|
name: string .
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Preds))
|
|
require.Equal(t, &pb.SchemaUpdate{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
ValueType: 9,
|
|
}, result.Preds[0])
|
|
require.Equal(t, 1, len(result.Types))
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.AttrInRootNamespace("Person"),
|
|
Fields: []*pb.SchemaUpdate{
|
|
{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
},
|
|
},
|
|
}, result.Types[0])
|
|
}
|
|
|
|
func TestParseMultipleTypes(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
type Person {
|
|
name
|
|
}
|
|
type Animal {
|
|
name
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(result.Types))
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.AttrInRootNamespace("Person"),
|
|
Fields: []*pb.SchemaUpdate{
|
|
{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
},
|
|
},
|
|
}, result.Types[0])
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.AttrInRootNamespace("Animal"),
|
|
Fields: []*pb.SchemaUpdate{
|
|
{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
},
|
|
},
|
|
}, result.Types[1])
|
|
}
|
|
|
|
func TestParseTypeDuplicateFields(t *testing.T) {
|
|
reset()
|
|
_, err := Parse(`
|
|
type Person {
|
|
name
|
|
name
|
|
}
|
|
`)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Duplicate fields with name: name")
|
|
}
|
|
|
|
func TestOldTypeFormat(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
type Person {
|
|
name: [string!]!
|
|
address: string!
|
|
children: [Person]
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Types))
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.AttrInRootNamespace("Person"),
|
|
Fields: []*pb.SchemaUpdate{
|
|
{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
},
|
|
{
|
|
Predicate: x.AttrInRootNamespace("address"),
|
|
},
|
|
{
|
|
Predicate: x.AttrInRootNamespace("children"),
|
|
},
|
|
},
|
|
}, result.Types[0])
|
|
}
|
|
|
|
func TestOldAndNewTypeFormat(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
type Person {
|
|
name: [string!]!
|
|
address
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Types))
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.AttrInRootNamespace("Person"),
|
|
Fields: []*pb.SchemaUpdate{
|
|
{
|
|
Predicate: x.AttrInRootNamespace("name"),
|
|
},
|
|
{
|
|
Predicate: x.AttrInRootNamespace("address"),
|
|
},
|
|
},
|
|
}, result.Types[0])
|
|
}
|
|
|
|
func TestParseTypeErrMissingNewLine(t *testing.T) {
|
|
reset()
|
|
_, err := Parse(`
|
|
type Person {
|
|
}type Animal {}`)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Expected new line or EOF after type declaration")
|
|
}
|
|
|
|
func TestParseComments(t *testing.T) {
|
|
reset()
|
|
_, err := Parse(`
|
|
#
|
|
# This is a test
|
|
#
|
|
user: bool .
|
|
user.name: string @index(exact) . # this should be unique
|
|
user.email: string @index(exact) . # this should be unique and lower-cased
|
|
user.password: password .
|
|
user.code: string . # for password recovery (can be null)
|
|
node: bool .
|
|
node.hashid: string @index(exact) . # @username/hashid
|
|
node.owner: uid @reverse . # (can be null)
|
|
node.parent: uid . # [uid] (use facet)
|
|
node.xdata: string . # store custom json data
|
|
#
|
|
# End of test
|
|
#
|
|
`)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestParseCommentsNoop(t *testing.T) {
|
|
reset()
|
|
_, err := Parse(`
|
|
# Spicy jalapeno bacon ipsum dolor amet t-bone kevin spare ribs sausage jowl cow pastrami short.
|
|
# Leberkas alcatra kielbasa chicken pastrami swine bresaola. Spare ribs landjaeger meatloaf.
|
|
# Chicken biltong boudin porchetta jowl swine burgdoggen cow kevin ground round landjaeger ham.
|
|
# Tongue buffalo cow filet mignon boudin sirloin pancetta pork belly beef ribs. Cow landjaeger.
|
|
`)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestParseCommentsErrMissingType(t *testing.T) {
|
|
reset()
|
|
_, err := Parse(`
|
|
# The definition below should trigger an error
|
|
# because we commented out its type.
|
|
node: # bool .
|
|
`)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Missing Type")
|
|
}
|
|
|
|
func TestParseTypeComments(t *testing.T) {
|
|
reset()
|
|
_, err := Parse(`
|
|
# User is a service user
|
|
type User {
|
|
# TODO: add more fields
|
|
name # e.g., srfrog
|
|
# expanded comment
|
|
# embedded # comments # here
|
|
}
|
|
# /User
|
|
`)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestParseWithNamespace(t *testing.T) {
|
|
reset()
|
|
result, err := Parse(`
|
|
[10] jobs: [string] @index(term) .
|
|
[0x123] occupations: [string] .
|
|
[0xf2] graduation: [dateTime] .
|
|
[0xf2] type Person {
|
|
name: [string!]!
|
|
address: string!
|
|
children: [Person]
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(result.Preds))
|
|
require.EqualValues(t, &pb.SchemaUpdate{
|
|
Predicate: x.NamespaceAttr(10, "jobs"),
|
|
ValueType: 9,
|
|
Directive: pb.SchemaUpdate_INDEX,
|
|
Tokenizer: []string{"term"},
|
|
List: true,
|
|
}, result.Preds[0])
|
|
|
|
require.EqualValues(t, &pb.SchemaUpdate{
|
|
Predicate: x.NamespaceAttr(0x123, "occupations"),
|
|
ValueType: 9,
|
|
List: true,
|
|
}, result.Preds[1])
|
|
|
|
require.EqualValues(t, &pb.SchemaUpdate{
|
|
Predicate: x.NamespaceAttr(0xf2, "graduation"),
|
|
ValueType: 5,
|
|
List: true,
|
|
}, result.Preds[2])
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(result.Types))
|
|
require.Equal(t, &pb.TypeUpdate{
|
|
TypeName: x.NamespaceAttr(0xf2, "Person"),
|
|
Fields: []*pb.SchemaUpdate{
|
|
{
|
|
Predicate: x.NamespaceAttr(0xf2, "name"),
|
|
},
|
|
{
|
|
Predicate: x.NamespaceAttr(0xf2, "address"),
|
|
},
|
|
{
|
|
Predicate: x.NamespaceAttr(0xf2, "children"),
|
|
},
|
|
},
|
|
}, result.Types[0])
|
|
}
|
|
|
|
var ps *badger.DB
|
|
|
|
func TestMain(m *testing.M) {
|
|
dir, err := os.MkdirTemp("", "storetest_")
|
|
x.Panic(err)
|
|
defer os.RemoveAll(dir)
|
|
|
|
kvOpt := badger.DefaultOptions(dir)
|
|
ps, err = badger.OpenManaged(kvOpt)
|
|
x.Panic(err)
|
|
defer ps.Close()
|
|
|
|
Init(ps)
|
|
m.Run()
|
|
}
|