Skip to content

Commit

Permalink
Entity resolver tests (#1697)
Browse files Browse the repository at this point in the history
* Moving federation tests to their own folders

Reorganizing the tests in the federation plugin a little bit so make it simpler to add more safely without testdata colliding. This is in anticipation for a follow up PR for adding entity resolver tests.

Run the tests with `go test ./plugin/federation/...` and verify they all pass. Also verify that the testdata/allthething directory has a `generated` directory specific to that test.

NOTE: There is a catch all type of test that I moved to the directory `allthething`.  Open to suggestions for a better name! One potential thing to considere here is to split up the tests that use that testdata and break them down into more specific tests. E.g. Add a multikey test in the testdata/entity.  For now, Im leaving that as a TODO.

* Adding entity resolver tests in the federation plugin

The tests work by sending `_entities` queries with `representation` variables directly to the mocked server, which will allow us to test generated federation code end to end.  For context, the format of the entity query is something like:

```
query($representations:[_Any!]!){_entities(representations:$representations){ ...on Hello{secondary} }}
```

And `representations` are the list of federated keys for the entities being resovled, and they look like

```
representations: [{
   "__typename": "Hello",
   "name":       "federated key value 1",
}, {
   "__typename": "Hello",
   "name":       "federated key value 2",
}]
```

The entity resolver tests are in `plugin/federation/federation_entityresolver_test.go` and they rely on `plugin/federation/testdata/entityresolver`.

To run the tests:
1. Build the entityresolver testdata
  - From plugin/federation, run `go run github.com/99designs/gqlgen --config testdata/entityresolver/gqlgen.yml`
2. Run the tests with `go test ./...` or similar
  • Loading branch information
MiguelCastillo authored Nov 9, 2021
1 parent b7db36d commit 01d3c4f
Show file tree
Hide file tree
Showing 18 changed files with 3,925 additions and 14 deletions.
102 changes: 102 additions & 0 deletions plugin/federation/federation_entityresolver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package federation

import (
"strings"
"testing"

"github.com/99designs/gqlgen/client"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/stretchr/testify/require"

"github.com/99designs/gqlgen/plugin/federation/testdata/entityresolver"
"github.com/99designs/gqlgen/plugin/federation/testdata/entityresolver/generated"
)

func TestEntityResolver(t *testing.T) {
c := client.New(handler.NewDefaultServer(
generated.NewExecutableSchema(generated.Config{
Resolvers: &entityresolver.Resolver{}}),
))

t.Run("Hello entities - single federation key", func(t *testing.T) {
representations := []map[string]interface{}{
{
"__typename": "Hello",
"name": "first name - 1",
}, {
"__typename": "Hello",
"name": "first name - 2",
},
}

var resp struct {
Entities []struct {
Name string `json:"name"`
} `json:"_entities"`
}

err := c.Post(
entityQuery([]string{
"Hello {name}",
}),
&resp,
client.Var("representations", representations),
)

require.NoError(t, err)
require.Equal(t, resp.Entities[0].Name, "first name - 1")
require.Equal(t, resp.Entities[1].Name, "first name - 2")
})

t.Run("World entity with nested key", func(t *testing.T) {
representations := []map[string]interface{}{
{
"__typename": "World",
"hello": map[string]interface{}{
"name": "world name - 1",
},
"foo": "foo 1",
}, {
"__typename": "World",
"hello": map[string]interface{}{
"name": "world name - 2",
},
"foo": "foo 2",
},
}

var resp struct {
Entities []struct {
Foo string `json:"foo"`
Hello struct {
Name string `json:"name"`
} `json:"hello"`
} `json:"_entities"`
}

err := c.Post(
entityQuery([]string{
"World {foo hello {name}}",
}),
&resp,
client.Var("representations", representations),
)

require.NoError(t, err)
require.Equal(t, resp.Entities[0].Foo, "foo 1")
require.Equal(t, resp.Entities[0].Hello.Name, "world name - 1")
require.Equal(t, resp.Entities[1].Foo, "foo 2")
require.Equal(t, resp.Entities[1].Hello.Name, "world name - 2")
})
}

func entityQuery(queries []string) string {
// What we want!
//query($representations:[_Any!]!){_entities(representations:$representations){ ...on Hello{secondary} }}
entityQueries := make([]string, len(queries))
for i, query := range queries {
entityQueries[i] = " ... on " + query
}

return "query($representations:[_Any!]!){_entities(representations:$representations){" + strings.Join(entityQueries, "") + "}}"
}
5 changes: 3 additions & 2 deletions plugin/federation/federation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func TestWithEntities(t *testing.T) {
f, cfg := load(t, "test_data/gqlgen.yml")
f, cfg := load(t, "testdata/allthethings/gqlgen.yml")

require.Equal(t, []string{"ExternalExtension", "Hello", "MoreNesting", "NestedKey", "VeryNestedKey", "World"}, cfg.Schema.Types["_Entity"].Types)

Expand Down Expand Up @@ -70,10 +70,11 @@ func TestWithEntities(t *testing.T) {
}

func TestNoEntities(t *testing.T) {
f, cfg := load(t, "test_data/nokey.yml")
f, cfg := load(t, "testdata/entities/nokey.yml")

err := f.MutateConfig(cfg)
require.NoError(t, err)
require.Len(t, f.Entities, 0)
}

func load(t *testing.T, name string) (*federation, *config.Config) {
Expand Down
17 changes: 17 additions & 0 deletions plugin/federation/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Federation plugin

Add support for graphql federation in your graphql Go server!

TODO(miguel): add details.

# Tests
There are several different tests. Some will process the configuration file directly. You can see those in the `federation_test.go`. There are also tests for entity resolvers, which will simulate requests from a federation server like Apollo Federation.

Running entity resolver tests.
1. Go to `plugin/federation`
2. Run the command `go run github.com/99designs/gqlgen --config testdata/entityresolver/gqlgen.yml`
3. Run the tests with `go test ./...`.

# Architecture

TODO(miguel): add details.
6 changes: 0 additions & 6 deletions plugin/federation/test_data/gqlgen.yml

This file was deleted.

6 changes: 0 additions & 6 deletions plugin/federation/test_data/nokey.yml

This file was deleted.

9 changes: 9 additions & 0 deletions plugin/federation/testdata/allthethings/gqlgen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
schema:
- "testdata/allthethings/schema.graphql"
exec:
filename: testdata/allthethings/generated/exec.go
federation:
filename: testdata/allthethings/generated/federation.go

autobind:
- "github.com/99designs/gqlgen/plugin/federation/testdata/allthethings/model"
48 changes: 48 additions & 0 deletions plugin/federation/testdata/allthethings/model/federation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package model

type _FieldSet string //nolint:deadcode,unused

type Hello struct {
Name string
Secondary string
}

func (Hello) IsEntity() {}

type World struct {
Foo string
Bar int
}

func (World) IsEntity() {}

type ExternalExtension struct {
UPC string
Reviews []*World
}

func (ExternalExtension) IsEntity() {}

type NestedKey struct {
ID string
Hello *Hello
}

func (NestedKey) IsEntity() {}

type MoreNesting struct {
ID string
World *World
}

func (MoreNesting) IsEntity() {}

type VeryNestedKey struct {
ID string
Hello *Hello
World *World
Nested *NestedKey
More *MoreNesting
}

func (VeryNestedKey) IsEntity() {}
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions plugin/federation/testdata/entities/nokey.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
schema:
- "testdata/entities/nokey.graphql"
exec:
filename: testdata/entities/generated/exec.go
federation:
filename: testdata/entities/generated/federation.go
30 changes: 30 additions & 0 deletions plugin/federation/testdata/entityresolver/entity.resolvers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package entityresolver

// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.

import (
"context"

"github.com/99designs/gqlgen/plugin/federation/testdata/entityresolver/generated"
)

func (r *entityResolver) FindHelloByName(ctx context.Context, name string) (*generated.Hello, error) {
return &generated.Hello{
Name: name,
}, nil
}

func (r *entityResolver) FindWorldByHelloNameAndFoo(ctx context.Context, helloName string, foo string) (*generated.World, error) {
return &generated.World{
Hello: &generated.Hello{
Name: helloName,
},
Foo: foo,
}, nil
}

// Entity returns generated.EntityResolver implementation.
func (r *Resolver) Entity() generated.EntityResolver { return &entityResolver{r} }

type entityResolver struct{ *Resolver }
Loading

0 comments on commit 01d3c4f

Please sign in to comment.