diff --git a/dgraph/cmd/alpha/upsert_test.go b/dgraph/cmd/alpha/upsert_test.go index 67064555faa..0bf39b0eda4 100644 --- a/dgraph/cmd/alpha/upsert_test.go +++ b/dgraph/cmd/alpha/upsert_test.go @@ -18,8 +18,12 @@ package alpha import ( "strings" + "sync" "testing" + "github.com/dgraph-io/dgraph/z" + + "github.com/dgraph-io/dgo/y" "github.com/stretchr/testify/require" ) @@ -156,3 +160,598 @@ func TestUpsertExample0JSON(t *testing.T) { require.NoError(t, err) require.Contains(t, res, "Ashish") } + +func TestUpsertNoVarErr(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +friend: uid @reverse .`)) + + m1 := ` +upsert { + mutation { + set { + _:user1 "45" . + } + } + + query { + me(func: eq(age, 34)) { + ...fragmentA + friend { + ...fragmentA + age + } + } + } + + fragment fragmentA { + uid + } +}` + _, _, _, err := mutationWithTs(m1, "application/rdf", false, true, true, 0) + require.Contains(t, err.Error(), "upsert query op has no variables") +} + +func TestUpsertWithFragment(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +friend: uid @reverse .`)) + + m1 := ` +upsert { + mutation { + set { + uid(variable) "45" . + } + } + + query { + me(func: eq(age, 34)) { + friend { + ...fragmentA + } + } + } + + fragment fragmentA { + variable as uid + } +}` + keys, preds, _, err := mutationWithTs(m1, "application/rdf", false, true, true, 0) + require.NoError(t, err) + require.True(t, 0 == len(keys)) + require.True(t, contains(preds, "age")) + + // Ensure that another run works too + keys, preds, _, err = mutationWithTs(m1, "application/rdf", false, true, true, 0) + require.NoError(t, err) + require.True(t, 0 == len(keys)) + require.True(t, contains(preds, "age")) +} + +func TestUpsertInvalidErr(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +name: string @index(exact) . +friend: uid @reverse .`)) + + m1 := ` +{ + set { + uid(variable) "45" . + } +}` + _, _, _, err := mutationWithTs(m1, "application/rdf", false, true, true, 0) + require.Contains(t, err.Error(), "invalid syntax") +} + +func TestUpsertUndefinedVarErr(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +name: string @index(exact) . +friend: uid @reverse .`)) + + m1 := ` +upsert { + mutation { + set { + uid(42) "45" . + uid(variable) "45" . + } + } + + query { + me(func: eq(age, 34)) { + friend { + ...fragmentA + } + } + } + + fragment fragmentA { + variable as uid + } +}` + _, _, _, err := mutationWithTs(m1, "application/rdf", false, true, true, 0) + require.Contains(t, err.Error(), "Some variables are used but not defined") + require.Contains(t, err.Error(), "Defined:[variable]") + require.Contains(t, err.Error(), "Used:[42 variable]") +} + +func TestUpsertUnusedVarErr(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +name: string @index(exact) . +friend: uid @reverse .`)) + + m1 := ` +upsert { + mutation { + set { + uid(var2) "45" . + } + } + + query { + me(func: eq(age, 34)) { + var2 as uid + friend { + ...fragmentA + } + } + } + + fragment fragmentA { + var1 as uid + name + } +}` + _, _, _, err := mutationWithTs(m1, "application/rdf", false, true, true, 0) + require.Contains(t, err.Error(), "Some variables are defined but not used") + require.Contains(t, err.Error(), "Defined:[var1 var2]") + require.Contains(t, err.Error(), "Used:[var2]") +} + +func TestUpsertExampleNode(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +name: string @index(exact) @lang . +friend: uid @reverse .`)) + + m0 := ` +{ + set { + _:user1 "23" . + _:user1 "user1" . + _:user2 "34" . + _:user2 "user2" . + _:user3 "56" . + _:user3 "user3" . + } +}` + _, _, _, err := mutationWithTs(m0, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + m1 := ` +upsert { + mutation { + set { + uid( u) "true" . + } + } + + query { + var(func: has(age)) { + a as age + } + + oldest(func: uid(a), orderdesc: val(a), first: 1) { + u as uid + name + age + } + } +}` + _, _, _, err = mutationWithTs(m1, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + q1 := ` +{ + q(func: has(oldest)) { + name@en + age + oldest + } +}` + res, _, err := queryWithTs(q1, "application/graphql+-", 0) + require.NoError(t, err) + require.Contains(t, res, "user3") + require.Contains(t, res, "56") + require.Contains(t, res, "true") + + m2 := ` +upsert { + mutation { + delete { + uid (u1) * . + } + } + + query { + user1(func: eq(name@en, "user1")) { + u1 as uid + } + } +}` + _, _, _, err = mutationWithTs(m2, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + q2 := ` +{ + q(func: eq(name@en, "user1")) { + name@en + age + } +}` + res, _, err = queryWithTs(q2, "application/graphql+-", 0) + require.NoError(t, err) + require.NotContains(t, res, "user1") +} + +func TestUpsertExampleEdge(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +name: string @index(exact) @lang . +friend: uid @reverse .`)) + + m0 := ` +{ + set { + _:user1 "23" . + _:user1 "user1" . + _:user2 "34" . + _:user2 "user2" . + _:user3 "56" . + _:user3 "user3" . + } +}` + _, _, _, err := mutationWithTs(m0, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + m1 := ` +upsert { + mutation { + set { + uid ( u1 ) uid ( u2 ) . + } + } + + query { + user1(func: eq(name@en, "user1")) { + u1 as uid + } + + user2(func: eq(name@en, "user2")) { + u2 as uid + } + } +}` + _, _, _, err = mutationWithTs(m1, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + q1 := ` +{ + q(func: eq(name@en, "user1")) { + friend { + name@en + } + } +}` + res, _, err := queryWithTs(q1, "application/graphql+-", 0) + require.NoError(t, err) + require.Contains(t, res, "user2") + + m2 := ` +upsert { + mutation { + delete { + uid (u1) uid ( u2 ) . + } + } + + query { + user1(func: eq(name@en, "user1")) { + u1 as uid + } + + user2(func: eq(name@en, "user2")) { + u2 as uid + } + } +}` + _, _, _, err = mutationWithTs(m2, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + q2 := ` +{ + q(func: eq(name@en, "user1")) { + friend { + name@en + } + } +}` + res, _, err = queryWithTs(q2, "application/graphql+-", 0) + require.NoError(t, err) + require.NotContains(t, res, "user2") +} + +func TestUpsertExampleNodeJSON(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +name: string @index(exact) @lang . +friend: uid @reverse .`)) + + m0 := ` +{ + set { + _:user1 "23" . + _:user1 "user1" . + _:user2 "34" . + _:user2 "user2" . + _:user3 "56" . + _:user3 "user3" . + } +}` + _, _, _, err := mutationWithTs(m0, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + m1 := ` +{ + "set": [ + { + "uid": "uid(u)", + "oldest": "true" + } + ], + + "query": "{var(func: has(age)) {a as age} oldest(func: uid(a), orderdesc: val(a), first: 1) {u as uid}}" +}` + _, _, _, err = mutationWithTs(m1, "application/json", false, true, true, 0) + require.NoError(t, err) + + q1 := ` +{ + q(func: has(oldest)) { + name@en + age + oldest + } +}` + res, _, err := queryWithTs(q1, "application/graphql+-", 0) + require.NoError(t, err) + require.Contains(t, res, "user3") + require.Contains(t, res, "56") + require.Contains(t, res, "true") + + m2 := ` +{ + "delete": [ + { + "uid": "uid (u1)", + "name": null + } + ], + + "query": "{user1(func: eq(name@en, \"user1\")) {u1 as uid}}" +}` + _, _, _, err = mutationWithTs(m2, "application/json", false, true, true, 0) + require.NoError(t, err) + + q2 := ` +{ + q(func: eq(name@en, "user1")) { + name@en + age + } +}` + res, _, err = queryWithTs(q2, "application/graphql+-", 0) + require.NoError(t, err) + require.NotContains(t, res, "user1") +} + +func TestUpsertExampleEdgeJSON(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +age: int @index(int) . +name: string @index(exact) @lang . +friend: uid @reverse .`)) + + m0 := ` +{ + set { + _:user1 "23" . + _:user1 "user1" . + _:user2 "34" . + _:user2 "user2" . + _:user3 "56" . + _:user3 "user3" . + } +}` + _, _, _, err := mutationWithTs(m0, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + m1 := ` +{ + "set": [ + { + "uid": "uid(u1)", + "friend": "uid (u2 ) " + } + ], + + "query": "{user1(func: eq(name@en, \"user1\")) {u1 as uid} user2(func: eq(name@en, \"user2\")) {u2 as uid}}" +}` + _, _, _, err = mutationWithTs(m1, "application/json", false, true, true, 0) + require.NoError(t, err) + + q1 := ` +{ + q(func: eq(name@en, "user1")) { + friend { + name@en + } + } +}` + res, _, err := queryWithTs(q1, "application/graphql+-", 0) + require.NoError(t, err) + require.Contains(t, res, "user2") + + m3 := ` +{ + "delete": [ + { + "uid": "uid (u1)", + "friend": "uid ( u2 )" + } + ], + + "query": "{user1(func: eq(name@en, \"user1\")) {u1 as uid} user2(func: eq(name@en, \"user2\")) {u2 as uid}}" +}` + _, _, _, err = mutationWithTs(m3, "application/json", false, true, true, 0) + require.NoError(t, err) + + q3 := ` +{ + q(func: eq(name@en, "user1")) { + friend { + name@en + } + } +}` + res, _, err = queryWithTs(q3, "application/graphql+-", 0) + require.NoError(t, err) + require.NotContains(t, res, "user2") +} + +func TestUpsertBlankNodeWithVar(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(`name: string @index(exact) @lang .`)) + + m := ` +upsert { + mutation { + set { + uid(u) "user1" . + _:u "user2" . + } + } + + query { + users(func: eq(name, "user1")) { + u as uid + } + } +}` + _, _, _, err := mutationWithTs(m, "application/rdf", false, true, true, 0) + require.NoError(t, err) + + q := ` +{ + users(func: has(name)) { + uid + name + } +}` + res, _, err := queryWithTs(q, "application/graphql+-", 0) + require.NoError(t, err) + require.Contains(t, res, "user1") + require.Contains(t, res, "user2") +} + +func TestUpsertParallel(t *testing.T) { + require.NoError(t, dropAll()) + require.NoError(t, alterSchema(` +email: string @index(exact) @upsert . +name: string @index(exact) @lang . +friend: uid @reverse .`)) + + m := ` +upsert { + mutation { + set { + uid(u1) "user1@dgraph.io" . + uid(u1) "user1" . + uid(u2) "user2@dgraph.io" . + uid(u2) "user2" . + uid(u1) uid(u2) . + } + } + + query { + user1(func: eq(email, "user1@dgraph.io")) { + u1 as uid + } + + user2(func: eq(email, "user2@dgraph.io")) { + u2 as uid + } + } +}` + doUpsert := func(wg *sync.WaitGroup) { + defer wg.Done() + for i := 0; i < 10; i++ { + err := y.ErrAborted + for err != nil && strings.Contains(err.Error(), "Transaction has been aborted. Please retry") { + _, _, _, err = mutationWithTs(m, "application/rdf", false, true, true, 0) + } + + require.NoError(t, err) + } + } + + // 10 routines each doing parallel upsert 10 times + var wg sync.WaitGroup + wg.Add(10) + for i := 0; i < 10; i++ { + go doUpsert(&wg) + } + wg.Wait() + + q := ` +{ + user1(func: eq(email, "user1@dgraph.io")) { + name + email + friend { + name + email + } + } +}` + res, _, err := queryWithTs(q, "application/graphql+-", 0) + require.NoError(t, err) + expected := ` +{ + "data": { + "user1": [ + { + "name": "user1", + "email": "user1@dgraph.io", + "friend": { + "name": "user2", + "email": "user2@dgraph.io" + } + } + ] + } +}` + z.CompareJSON(t, expected, res) +}